mirror of
				https://github.com/bspeice/speice.io
				synced 2025-11-03 18:10:32 -05:00 
			
		
		
		
	Handle blog series
This commit is contained in:
		@ -1,11 +1,11 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
 | 
					slug: 2015/11/welcome
 | 
				
			||||||
title: Welcome, and an algorithm
 | 
					title: Welcome, and an algorithm
 | 
				
			||||||
date: 2015-11-19 12:00:00
 | 
					date: 2015-11-19 12:00:00
 | 
				
			||||||
last_update:
 | 
					last_update:
 | 
				
			||||||
    date: 2015-12-05 12:00:00
 | 
					    date: 2015-12-05 12:00:00
 | 
				
			||||||
slug: 2015/11/welcome
 | 
					 | 
				
			||||||
authors: [bspeice]
 | 
					authors: [bspeice]
 | 
				
			||||||
tags: [trading]
 | 
					tags: []
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Hello! Glad to meet you. I'm currently a student at Columbia University studying Financial Engineering, and want to give an overview of the projects I'm working on!
 | 
					Hello! Glad to meet you. I'm currently a student at Columbia University studying Financial Engineering, and want to give an overview of the projects I'm working on!
 | 
				
			||||||
@ -17,7 +17,7 @@ To start things off, Columbia has been hosting a trading competition that myself
 | 
				
			|||||||
The competition is scored in 3 areas:
 | 
					The competition is scored in 3 areas:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Total return
 | 
					- Total return
 | 
				
			||||||
- [Sharpe ratio](1)
 | 
					- [Sharpe ratio](https://en.wikipedia.org/wiki/Sharpe_ratio)
 | 
				
			||||||
- Maximum drawdown
 | 
					- Maximum drawdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Our algorithm uses a basic momentum strategy: in the given list of potential portfolios, pick the stocks that have been performing well in the past 30 days. Then, optimize for return subject to the drawdown being below a specific level. We didn't include the Sharpe ratio as a constraint, mostly because we were a bit late entering the competition.
 | 
					Our algorithm uses a basic momentum strategy: in the given list of potential portfolios, pick the stocks that have been performing well in the past 30 days. Then, optimize for return subject to the drawdown being below a specific level. We didn't include the Sharpe ratio as a constraint, mostly because we were a bit late entering the competition.
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
slug: 2018/01/captains-cookbook-part-1
 | 
					slug: 2018/01/captains-cookbook-part-1
 | 
				
			||||||
title: Captain's cookbook - part 1
 | 
					title: "Captain's Cookbook: Project setup"
 | 
				
			||||||
date: 2018-01-16 12:00:00
 | 
					date: 2018-01-16 12:00:00
 | 
				
			||||||
authors: [bspeice]
 | 
					authors: [bspeice]
 | 
				
			||||||
tags: []
 | 
					tags: []
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
slug: 2018/01/captains-cookbook-part-2
 | 
					slug: 2018/01/captains-cookbook-part-2
 | 
				
			||||||
title: Captain's cookbook - part 2
 | 
					title: "Captain's Cookbook: Practical usage"
 | 
				
			||||||
date: 2018-01-16 13:00:00
 | 
					date: 2018-01-16 13:00:00
 | 
				
			||||||
authors: [bspeice]
 | 
					authors: [bspeice]
 | 
				
			||||||
tags: []
 | 
					tags: []
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
slug: 2018/06/dateutil-parser-to-rust
 | 
					slug: 2018/06/dateutil-parser-to-rust
 | 
				
			||||||
title: "What I Learned: Porting Dateutil Parser to Rust"
 | 
					title: "What I learned porting dateutil to Rust"
 | 
				
			||||||
date: 2018-06-25 12:00:00
 | 
					date: 2018-06-25 12:00:00
 | 
				
			||||||
authors: [bspeice]
 | 
					authors: [bspeice]
 | 
				
			||||||
tags: []
 | 
					tags: []
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										218
									
								
								blog/2018-12-15-allocation-safety/_article.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								blog/2018-12-15-allocation-safety/_article.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,218 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					layout: post
 | 
				
			||||||
 | 
					title: "QADAPT - debug_assert! for your memory usage"
 | 
				
			||||||
 | 
					description: "...and why you want an allocator that goes 💥."
 | 
				
			||||||
 | 
					category:
 | 
				
			||||||
 | 
					tags: []
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I think it's part of the human condition to ignore perfectly good advice when it comes our way. A
 | 
				
			||||||
 | 
					bit over a month ago, I was dispensing sage wisdom for the ages:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> I had a really great idea: build a custom allocator that allows you to track your own allocations.
 | 
				
			||||||
 | 
					> I gave it a shot, but learned very quickly: **never write your own allocator.**
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					> -- [me](/2018/10/case-study-optimization.html)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I proceeded to ignore it, because we never really learn from our mistakes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There's another part of the human condition that derives joy from seeing things explode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<iframe src="https://giphy.com/embed/YA6dmVW0gfIw8" width="480" height="336" frameBorder="0"></iframe>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					And _that's_ the part I'm going to focus on.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Why an Allocator?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So why, after complaining about allocators, would I still want to write one? There are three reasons
 | 
				
			||||||
 | 
					for that:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Allocation/dropping is slow
 | 
				
			||||||
 | 
					2. It's difficult to know exactly when Rust will allocate or drop, especially when using code that
 | 
				
			||||||
 | 
					   you did not write
 | 
				
			||||||
 | 
					3. I want automated tools to verify behavior, instead of inspecting by hand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When I say "slow," it's important to define the terms. If you're writing web applications, you'll
 | 
				
			||||||
 | 
					spend orders of magnitude more time waiting for the database than you will the allocator. However,
 | 
				
			||||||
 | 
					there's still plenty of code where micro- or nano-seconds matter; think
 | 
				
			||||||
 | 
					[finance](https://www.youtube.com/watch?v=NH1Tta7purM),
 | 
				
			||||||
 | 
					[real-time audio](https://www.reddit.com/r/rust/comments/9hg7yj/synthesizer_progress_update/e6c291f),
 | 
				
			||||||
 | 
					[self-driving cars](https://polysync.io/blog/session-types-for-hearty-codecs/), and
 | 
				
			||||||
 | 
					[networking](https://carllerche.github.io/bytes/bytes/index.html). In these situations it's simply
 | 
				
			||||||
 | 
					unacceptable for you to spend time doing things that are not your program, and waiting on the
 | 
				
			||||||
 | 
					allocator is not cool.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					As I continue to learn Rust, it's difficult for me to predict where exactly allocations will happen.
 | 
				
			||||||
 | 
					So, I propose we play a quick trivia game: **Does this code invoke the allocator?**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Example 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					fn my_function() {
 | 
				
			||||||
 | 
					    let v: Vec<u8> = Vec::new();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**No**: Rust [knows how big](https://doc.rust-lang.org/std/mem/fn.size_of.html) the `Vec` type is,
 | 
				
			||||||
 | 
					and reserves a fixed amount of memory on the stack for the `v` vector. However, if we wanted to
 | 
				
			||||||
 | 
					reserve extra space (using `Vec::with_capacity`) the allocator would get invoked.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Example 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					fn my_function() {
 | 
				
			||||||
 | 
					    let v: Box<Vec<u8>> = Box::new(Vec::new());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Yes**: Because Boxes allow us to work with things that are of unknown size, it has to allocate on
 | 
				
			||||||
 | 
					the heap. While the `Box` is unnecessary in this snippet (release builds will optimize out the
 | 
				
			||||||
 | 
					allocation), reserving heap space more generally is needed to pass a dynamically sized type to
 | 
				
			||||||
 | 
					another function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Example 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					fn my_function(v: Vec<u8>) {
 | 
				
			||||||
 | 
					    v.push(5);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Maybe**: Depending on whether the Vector we were given has space available, we may or may not
 | 
				
			||||||
 | 
					allocate. Especially when dealing with code that you did not author, it's difficult to verify that
 | 
				
			||||||
 | 
					things behave as you expect them to.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Blowing Things Up
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So, how exactly does QADAPT solve these problems? **Whenever an allocation or drop occurs in code
 | 
				
			||||||
 | 
					marked allocation-safe, QADAPT triggers a thread panic.** We don't want to let the program continue
 | 
				
			||||||
 | 
					as if nothing strange happened, _we want things to explode_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					However, you don't want code to panic in production because of circumstances you didn't predict.
 | 
				
			||||||
 | 
					Just like [`debug_assert!`](https://doc.rust-lang.org/std/macro.debug_assert.html), **QADAPT will
 | 
				
			||||||
 | 
					strip out its own code when building in release mode to guarantee no panics and no performance
 | 
				
			||||||
 | 
					impact.**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Finally, there are three ways to have QADAPT check that your code will not invoke the allocator:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Using a procedural macro
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The easiest method, watch an entire function for allocator invocation:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use qadapt::no_alloc;
 | 
				
			||||||
 | 
					use qadapt::QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[global_allocator]
 | 
				
			||||||
 | 
					static Q: QADAPT = QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[no_alloc]
 | 
				
			||||||
 | 
					fn push_vec(v: &mut Vec<u8>) {
 | 
				
			||||||
 | 
					    // This triggers a panic if v.len() == v.capacity()
 | 
				
			||||||
 | 
					    v.push(5);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let v = Vec::with_capacity(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This will *not* trigger a panic
 | 
				
			||||||
 | 
					    push_vec(&v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This *will* trigger a panic
 | 
				
			||||||
 | 
					    push_vec(&v);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Using a regular macro
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For times when you need more precision:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use qadapt::assert_no_alloc;
 | 
				
			||||||
 | 
					use qadapt::QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[global_allocator]
 | 
				
			||||||
 | 
					static Q: QADAPT = QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let v = Vec::with_capacity(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // No allocations here, we already have space reserved
 | 
				
			||||||
 | 
					    assert_no_alloc!(v.push(5));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Even though we remove an item, it doesn't trigger a drop
 | 
				
			||||||
 | 
					    // because it's a scalar. If it were a `Box<_>` type,
 | 
				
			||||||
 | 
					    // a drop would trigger.
 | 
				
			||||||
 | 
					    assert_no_alloc!({
 | 
				
			||||||
 | 
					        v.pop().unwrap();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Using function calls
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Both the most precise and most tedious:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use qadapt::enter_protected;
 | 
				
			||||||
 | 
					use qadapt::exit_protected;
 | 
				
			||||||
 | 
					use qadapt::QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[global_allocator]
 | 
				
			||||||
 | 
					static Q: QADAPT = QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    // This triggers an allocation (on non-release builds)
 | 
				
			||||||
 | 
					    let v = Vec::with_capacity(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enter_protected();
 | 
				
			||||||
 | 
					    // This does not trigger an allocation because we've reserved size
 | 
				
			||||||
 | 
					    v.push(0);
 | 
				
			||||||
 | 
					    exit_protected();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This triggers an allocation because we ran out of size,
 | 
				
			||||||
 | 
					    // but doesn't panic because we're no longer protected.
 | 
				
			||||||
 | 
					    v.push(1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Caveats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It's important to point out that QADAPT code is synchronous, so please be careful when mixing in
 | 
				
			||||||
 | 
					asynchronous functions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use futures::future::Future;
 | 
				
			||||||
 | 
					use futures::future::ok;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[no_alloc]
 | 
				
			||||||
 | 
					fn async_capacity() -> impl Future<Item=Vec<u8>, Error=()> {
 | 
				
			||||||
 | 
					    ok(12).and_then(|e| Ok(Vec::with_capacity(e)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    // This doesn't trigger a panic because the `and_then` closure
 | 
				
			||||||
 | 
					    // wasn't run during the function call.
 | 
				
			||||||
 | 
					    async_capacity();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Still no panic
 | 
				
			||||||
 | 
					    assert_no_alloc!(async_capacity());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This will panic because the allocation happens during `unwrap`
 | 
				
			||||||
 | 
					    // in the `assert_no_alloc!` macro
 | 
				
			||||||
 | 
					    assert_no_alloc!(async_capacity().poll().unwrap());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Conclusion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					While there's a lot more to writing high-performance code than managing your usage of the allocator,
 | 
				
			||||||
 | 
					it's critical that you do use the allocator correctly. QADAPT will verify that your code is doing
 | 
				
			||||||
 | 
					what you expect. It's usable even on stable Rust from version 1.31 onward, which isn't the case for
 | 
				
			||||||
 | 
					most allocators. Version 1.0 was released today, and you can check it out over at
 | 
				
			||||||
 | 
					[crates.io](https://crates.io/crates/qadapt) or on [github](https://github.com/bspeice/qadapt).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I'm hoping to write more about high-performance Rust in the future, and I expect that QADAPT will
 | 
				
			||||||
 | 
					help guide that. If there are topics you're interested in, let me know in the comments below!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[qadapt]: https://crates.io/crates/qadapt
 | 
				
			||||||
							
								
								
									
										222
									
								
								blog/2018-12-15-allocation-safety/index.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								blog/2018-12-15-allocation-safety/index.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,222 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					slug: 2018/12/allocation-safety
 | 
				
			||||||
 | 
					title: "QADAPT - debug_assert! for allocations"
 | 
				
			||||||
 | 
					date: 2018-12-15 12:00:00
 | 
				
			||||||
 | 
					authors: [bspeice]
 | 
				
			||||||
 | 
					tags: []
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I think it's part of the human condition to ignore perfectly good advice when it comes our way. A
 | 
				
			||||||
 | 
					bit over a month ago, I was dispensing sage wisdom for the ages:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> I had a really great idea: build a custom allocator that allows you to track your own allocations.
 | 
				
			||||||
 | 
					> I gave it a shot, but learned very quickly: **never write your own allocator.**
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					> -- [me](../2018-10-08-case-study-optimization)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I proceeded to ignore it, because we never really learn from our mistakes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!-- truncate -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There's another part of the human condition that derives joy from seeing things explode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<center>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</center>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					And _that's_ the part I'm going to focus on.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Why an Allocator?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So why, after complaining about allocators, would I still want to write one? There are three reasons
 | 
				
			||||||
 | 
					for that:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Allocation/dropping is slow
 | 
				
			||||||
 | 
					2. It's difficult to know exactly when Rust will allocate or drop, especially when using code that
 | 
				
			||||||
 | 
					   you did not write
 | 
				
			||||||
 | 
					3. I want automated tools to verify behavior, instead of inspecting by hand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When I say "slow," it's important to define the terms. If you're writing web applications, you'll
 | 
				
			||||||
 | 
					spend orders of magnitude more time waiting for the database than you will the allocator. However,
 | 
				
			||||||
 | 
					there's still plenty of code where micro- or nano-seconds matter; think
 | 
				
			||||||
 | 
					[finance](https://www.youtube.com/watch?v=NH1Tta7purM),
 | 
				
			||||||
 | 
					[real-time audio](https://www.reddit.com/r/rust/comments/9hg7yj/synthesizer_progress_update/e6c291f),
 | 
				
			||||||
 | 
					[self-driving cars](https://polysync.io/blog/session-types-for-hearty-codecs/), and
 | 
				
			||||||
 | 
					[networking](https://carllerche.github.io/bytes/bytes/index.html). In these situations it's simply
 | 
				
			||||||
 | 
					unacceptable for you to spend time doing things that are not your program, and waiting on the
 | 
				
			||||||
 | 
					allocator is not cool.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					As I continue to learn Rust, it's difficult for me to predict where exactly allocations will happen.
 | 
				
			||||||
 | 
					So, I propose we play a quick trivia game: **Does this code invoke the allocator?**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Example 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					fn my_function() {
 | 
				
			||||||
 | 
					    let v: Vec<u8> = Vec::new();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**No**: Rust [knows how big](https://doc.rust-lang.org/std/mem/fn.size_of.html) the `Vec` type is,
 | 
				
			||||||
 | 
					and reserves a fixed amount of memory on the stack for the `v` vector. However, if we wanted to
 | 
				
			||||||
 | 
					reserve extra space (using `Vec::with_capacity`) the allocator would get invoked.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Example 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					fn my_function() {
 | 
				
			||||||
 | 
					    let v: Box<Vec<u8>> = Box::new(Vec::new());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Yes**: Because Boxes allow us to work with things that are of unknown size, it has to allocate on
 | 
				
			||||||
 | 
					the heap. While the `Box` is unnecessary in this snippet (release builds will optimize out the
 | 
				
			||||||
 | 
					allocation), reserving heap space more generally is needed to pass a dynamically sized type to
 | 
				
			||||||
 | 
					another function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Example 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					fn my_function(v: Vec<u8>) {
 | 
				
			||||||
 | 
					    v.push(5);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Maybe**: Depending on whether the Vector we were given has space available, we may or may not
 | 
				
			||||||
 | 
					allocate. Especially when dealing with code that you did not author, it's difficult to verify that
 | 
				
			||||||
 | 
					things behave as you expect them to.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Blowing Things Up
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So, how exactly does QADAPT solve these problems? **Whenever an allocation or drop occurs in code
 | 
				
			||||||
 | 
					marked allocation-safe, QADAPT triggers a thread panic.** We don't want to let the program continue
 | 
				
			||||||
 | 
					as if nothing strange happened, _we want things to explode_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					However, you don't want code to panic in production because of circumstances you didn't predict.
 | 
				
			||||||
 | 
					Just like [`debug_assert!`](https://doc.rust-lang.org/std/macro.debug_assert.html), **QADAPT will
 | 
				
			||||||
 | 
					strip out its own code when building in release mode to guarantee no panics and no performance
 | 
				
			||||||
 | 
					impact.**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Finally, there are three ways to have QADAPT check that your code will not invoke the allocator:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Using a procedural macro
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The easiest method, watch an entire function for allocator invocation:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use qadapt::no_alloc;
 | 
				
			||||||
 | 
					use qadapt::QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[global_allocator]
 | 
				
			||||||
 | 
					static Q: QADAPT = QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[no_alloc]
 | 
				
			||||||
 | 
					fn push_vec(v: &mut Vec<u8>) {
 | 
				
			||||||
 | 
					    // This triggers a panic if v.len() == v.capacity()
 | 
				
			||||||
 | 
					    v.push(5);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let v = Vec::with_capacity(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This will *not* trigger a panic
 | 
				
			||||||
 | 
					    push_vec(&v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This *will* trigger a panic
 | 
				
			||||||
 | 
					    push_vec(&v);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Using a regular macro
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For times when you need more precision:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use qadapt::assert_no_alloc;
 | 
				
			||||||
 | 
					use qadapt::QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[global_allocator]
 | 
				
			||||||
 | 
					static Q: QADAPT = QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let v = Vec::with_capacity(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // No allocations here, we already have space reserved
 | 
				
			||||||
 | 
					    assert_no_alloc!(v.push(5));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Even though we remove an item, it doesn't trigger a drop
 | 
				
			||||||
 | 
					    // because it's a scalar. If it were a `Box<_>` type,
 | 
				
			||||||
 | 
					    // a drop would trigger.
 | 
				
			||||||
 | 
					    assert_no_alloc!({
 | 
				
			||||||
 | 
					        v.pop().unwrap();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Using function calls
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Both the most precise and most tedious:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use qadapt::enter_protected;
 | 
				
			||||||
 | 
					use qadapt::exit_protected;
 | 
				
			||||||
 | 
					use qadapt::QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[global_allocator]
 | 
				
			||||||
 | 
					static Q: QADAPT = QADAPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    // This triggers an allocation (on non-release builds)
 | 
				
			||||||
 | 
					    let v = Vec::with_capacity(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enter_protected();
 | 
				
			||||||
 | 
					    // This does not trigger an allocation because we've reserved size
 | 
				
			||||||
 | 
					    v.push(0);
 | 
				
			||||||
 | 
					    exit_protected();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This triggers an allocation because we ran out of size,
 | 
				
			||||||
 | 
					    // but doesn't panic because we're no longer protected.
 | 
				
			||||||
 | 
					    v.push(1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Caveats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It's important to point out that QADAPT code is synchronous, so please be careful when mixing in
 | 
				
			||||||
 | 
					asynchronous functions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use futures::future::Future;
 | 
				
			||||||
 | 
					use futures::future::ok;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[no_alloc]
 | 
				
			||||||
 | 
					fn async_capacity() -> impl Future<Item=Vec<u8>, Error=()> {
 | 
				
			||||||
 | 
					    ok(12).and_then(|e| Ok(Vec::with_capacity(e)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    // This doesn't trigger a panic because the `and_then` closure
 | 
				
			||||||
 | 
					    // wasn't run during the function call.
 | 
				
			||||||
 | 
					    async_capacity();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Still no panic
 | 
				
			||||||
 | 
					    assert_no_alloc!(async_capacity());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This will panic because the allocation happens during `unwrap`
 | 
				
			||||||
 | 
					    // in the `assert_no_alloc!` macro
 | 
				
			||||||
 | 
					    assert_no_alloc!(async_capacity().poll().unwrap());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Conclusion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					While there's a lot more to writing high-performance code than managing your usage of the allocator,
 | 
				
			||||||
 | 
					it's critical that you do use the allocator correctly. QADAPT will verify that your code is doing
 | 
				
			||||||
 | 
					what you expect. It's usable even on stable Rust from version 1.31 onward, which isn't the case for
 | 
				
			||||||
 | 
					most allocators. Version 1.0 was released today, and you can check it out over at
 | 
				
			||||||
 | 
					[crates.io](https://crates.io/crates/qadapt) or on [github](https://github.com/bspeice/qadapt).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I'm hoping to write more about high-performance Rust in the future, and I expect that QADAPT will
 | 
				
			||||||
 | 
					help guide that. If there are topics you're interested in, let me know in the comments below!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[qadapt]: https://crates.io/crates/qadapt
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								blog/2018-12-15-allocation-safety/watch-the-world-burn.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								blog/2018-12-15-allocation-safety/watch-the-world-burn.webp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.7 MiB  | 
@ -1,6 +1,8 @@
 | 
				
			|||||||
:root {
 | 
					:root {
 | 
				
			||||||
    --ifm-container-width: 1280px;
 | 
					    --ifm-container-width: 1280px;
 | 
				
			||||||
 | 
					    --ifm-container-width-xl: 1440px;
 | 
				
			||||||
    --ifm-footer-padding-vertical: .5rem;
 | 
					    --ifm-footer-padding-vertical: .5rem;
 | 
				
			||||||
 | 
					    --ifm-spacing-horizontal: .8rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.header-github-link:hover {
 | 
					.header-github-link:hover {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,29 +0,0 @@
 | 
				
			|||||||
import React from 'react';
 | 
					 | 
				
			||||||
import clsx from 'clsx';
 | 
					 | 
				
			||||||
import Layout from '@theme/Layout';
 | 
					 | 
				
			||||||
import BlogSidebar from '@theme/BlogSidebar';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import type {Props} from '@theme/BlogLayout';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function BlogLayout(props: Props): JSX.Element {
 | 
					 | 
				
			||||||
  const {sidebar, toc, children, ...layoutProps} = props;
 | 
					 | 
				
			||||||
  const hasSidebar = sidebar && sidebar.items.length > 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Layout {...layoutProps}>
 | 
					 | 
				
			||||||
      <div className="container margin-vert--lg">
 | 
					 | 
				
			||||||
        <div className="row">
 | 
					 | 
				
			||||||
          <BlogSidebar sidebar={sidebar} />
 | 
					 | 
				
			||||||
          <main
 | 
					 | 
				
			||||||
            className={clsx('col', {
 | 
					 | 
				
			||||||
              'col--8': hasSidebar,
 | 
					 | 
				
			||||||
              'col--10 col--offset-1': !hasSidebar,
 | 
					 | 
				
			||||||
            })}>
 | 
					 | 
				
			||||||
            {children}
 | 
					 | 
				
			||||||
          </main>
 | 
					 | 
				
			||||||
          {toc && <div className="col col--2">{toc}</div>}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </Layout>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,3 +1,7 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Docusaurus typically puts the newer post on the left button,
 | 
				
			||||||
 | 
					 * and older posts on the right. This file exists to swap them.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import Translate, {translate} from '@docusaurus/Translate';
 | 
					import Translate, {translate} from '@docusaurus/Translate';
 | 
				
			||||||
import PaginatorNavLink from '@theme/PaginatorNavLink';
 | 
					import PaginatorNavLink from '@theme/PaginatorNavLink';
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										120
									
								
								src/theme/BlogSidebar/Content/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/theme/BlogSidebar/Content/index.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,120 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Use post titles to infer blog post series
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import React, { memo, type ReactNode } from 'react';
 | 
				
			||||||
 | 
					import Heading, { HeadingType } from '@theme/Heading';
 | 
				
			||||||
 | 
					import type { Props } from '@theme/BlogSidebar/Content';
 | 
				
			||||||
 | 
					import { BlogSidebarItem } from '@docusaurus/plugin-content-blog';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function BlogSidebarGroup({ title, headingType, children }: { title: string, headingType: HeadingType, children: ReactNode }) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div role="group">
 | 
				
			||||||
 | 
					      <Heading as={headingType}>
 | 
				
			||||||
 | 
					        {title}
 | 
				
			||||||
 | 
					      </Heading>
 | 
				
			||||||
 | 
					      {children}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function groupBySeries(items: BlogSidebarItem[], ListComponent: Props["ListComponent"]) {
 | 
				
			||||||
 | 
					  var returnItems = [];
 | 
				
			||||||
 | 
					  var seriesItems: BlogSidebarItem[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function flushSeries() {
 | 
				
			||||||
 | 
					    if (seriesItems.length === 0) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const seriesTitle = seriesItems[0].title.split(":")[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Strip the series name from the titles
 | 
				
			||||||
 | 
					    seriesItems = seriesItems.map(item => {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        ...item,
 | 
				
			||||||
 | 
					        title: item.title.split(":")[1].trim(),
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Reverse the display ordering - normally blog items are shown in descending time order,
 | 
				
			||||||
 | 
					    // but for a series, we want to show ascending order
 | 
				
			||||||
 | 
					    seriesItems = seriesItems.reverse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    returnItems.push(<>
 | 
				
			||||||
 | 
					      <BlogSidebarGroup title={seriesTitle} headingType='h4'>
 | 
				
			||||||
 | 
					        <ul>
 | 
				
			||||||
 | 
					          <ListComponent items={seriesItems} />
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					      </BlogSidebarGroup>
 | 
				
			||||||
 | 
					    </>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    seriesItems = [];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const item of items) {
 | 
				
			||||||
 | 
					    // If this item is part of a series, begin accumulating
 | 
				
			||||||
 | 
					    if (item.title.includes(":")) {
 | 
				
			||||||
 | 
					      seriesItems.push(item);
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    flushSeries();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    returnItems.push(<ListComponent items={[item]} />);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  flushSeries();
 | 
				
			||||||
 | 
					  return returnItems;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function groupByYear(items: BlogSidebarItem[], ListComponent: Props["ListComponent"]) {
 | 
				
			||||||
 | 
					  var returnItems = [];
 | 
				
			||||||
 | 
					  var yearItems: BlogSidebarItem[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function flushSeries() {
 | 
				
			||||||
 | 
					    if (yearItems.length === 0) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const yearTitle = new Date(yearItems[0].date).getFullYear();
 | 
				
			||||||
 | 
					    const yearItemsGrouped = groupBySeries(yearItems, ListComponent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    returnItems.push(<>
 | 
				
			||||||
 | 
					      <BlogSidebarGroup title={String(yearTitle)} headingType='h3'>
 | 
				
			||||||
 | 
					        {yearItemsGrouped}
 | 
				
			||||||
 | 
					      </BlogSidebarGroup>
 | 
				
			||||||
 | 
					    </>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    yearItems = [];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const item of items) {
 | 
				
			||||||
 | 
					    if (yearItems.length === 0) {
 | 
				
			||||||
 | 
					      yearItems.push(item);
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const itemYear = new Date(item.date).getFullYear();
 | 
				
			||||||
 | 
					    const currentYear = new Date(yearItems[0].date).getFullYear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (itemYear !== currentYear) {
 | 
				
			||||||
 | 
					      flushSeries();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    yearItems.push(item);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  flushSeries();
 | 
				
			||||||
 | 
					  return returnItems;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function BlogSidebarContent({
 | 
				
			||||||
 | 
					  items,
 | 
				
			||||||
 | 
					  yearGroupHeadingClassName,
 | 
				
			||||||
 | 
					  ListComponent,
 | 
				
			||||||
 | 
					}: Props): ReactNode {
 | 
				
			||||||
 | 
					  return groupByYear(items, ListComponent);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default memo(BlogSidebarContent);
 | 
				
			||||||
@ -1,50 +0,0 @@
 | 
				
			|||||||
import React, {memo} from 'react';
 | 
					 | 
				
			||||||
import clsx from 'clsx';
 | 
					 | 
				
			||||||
import {translate} from '@docusaurus/Translate';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  useVisibleBlogSidebarItems,
 | 
					 | 
				
			||||||
  BlogSidebarItemList,
 | 
					 | 
				
			||||||
} from '@docusaurus/plugin-content-blog/client';
 | 
					 | 
				
			||||||
import BlogSidebarContent from '@theme/BlogSidebar/Content';
 | 
					 | 
				
			||||||
import type {Props as BlogSidebarContentProps} from '@theme/BlogSidebar/Content';
 | 
					 | 
				
			||||||
import type {Props} from '@theme/BlogSidebar/Desktop';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import styles from './styles.module.css';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const ListComponent: BlogSidebarContentProps['ListComponent'] = ({items}) => {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <BlogSidebarItemList
 | 
					 | 
				
			||||||
      items={items}
 | 
					 | 
				
			||||||
      ulClassName={clsx(styles.sidebarItemList, 'clean-list')}
 | 
					 | 
				
			||||||
      liClassName={styles.sidebarItem}
 | 
					 | 
				
			||||||
      linkClassName={styles.sidebarItemLink}
 | 
					 | 
				
			||||||
      linkActiveClassName={styles.sidebarItemLinkActive}
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function BlogSidebarDesktop({sidebar}: Props) {
 | 
					 | 
				
			||||||
  const items = useVisibleBlogSidebarItems(sidebar.items);
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <aside className="col col--2">
 | 
					 | 
				
			||||||
      <nav
 | 
					 | 
				
			||||||
        className={clsx(styles.sidebar, 'thin-scrollbar')}
 | 
					 | 
				
			||||||
        aria-label={translate({
 | 
					 | 
				
			||||||
          id: 'theme.blog.sidebar.navAriaLabel',
 | 
					 | 
				
			||||||
          message: 'Blog recent posts navigation',
 | 
					 | 
				
			||||||
          description: 'The ARIA label for recent posts in the blog sidebar',
 | 
					 | 
				
			||||||
        })}>
 | 
					 | 
				
			||||||
        <div className={clsx(styles.sidebarItemTitle, 'margin-bottom--md')}>
 | 
					 | 
				
			||||||
          {sidebar.title}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <BlogSidebarContent
 | 
					 | 
				
			||||||
          items={items}
 | 
					 | 
				
			||||||
          ListComponent={ListComponent}
 | 
					 | 
				
			||||||
          yearGroupHeadingClassName={styles.yearGroupHeading}
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
      </nav>
 | 
					 | 
				
			||||||
    </aside>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default memo(BlogSidebarDesktop);
 | 
					 | 
				
			||||||
@ -1,45 +0,0 @@
 | 
				
			|||||||
.sidebar {
 | 
					 | 
				
			||||||
  max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
 | 
					 | 
				
			||||||
  overflow-y: auto;
 | 
					 | 
				
			||||||
  position: sticky;
 | 
					 | 
				
			||||||
  top: calc(var(--ifm-navbar-height) + 2rem);
 | 
					 | 
				
			||||||
  padding-right: 1px;
 | 
					 | 
				
			||||||
  border-right: 1px solid var(--ifm-toc-border-color);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.sidebarItemTitle {
 | 
					 | 
				
			||||||
  font-size: var(--ifm-h3-font-size);
 | 
					 | 
				
			||||||
  font-weight: var(--ifm-font-weight-bold);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.sidebarItemList {
 | 
					 | 
				
			||||||
  font-size: 0.9rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.sidebarItem {
 | 
					 | 
				
			||||||
  margin-top: 0.7rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.sidebarItemLink {
 | 
					 | 
				
			||||||
  color: var(--ifm-font-color-base);
 | 
					 | 
				
			||||||
  display: block;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.sidebarItemLink:hover {
 | 
					 | 
				
			||||||
  text-decoration: none;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.sidebarItemLinkActive {
 | 
					 | 
				
			||||||
  color: var(--ifm-color-primary) !important;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@media (max-width: 996px) {
 | 
					 | 
				
			||||||
  .sidebar {
 | 
					 | 
				
			||||||
    display: none;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.yearGroupHeading {
 | 
					 | 
				
			||||||
  margin-top: 1.6rem;
 | 
					 | 
				
			||||||
  margin-bottom: 0.4rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user