1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
// Copyright 2016 LambdaStack All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::cell::RefCell; use std::fmt::{self, Write}; use std::str; use time::{self, Duration}; pub struct Now(()); /// Returns a struct, which when formatted, renders an appropriate `Date` header /// value. pub fn now() -> Now { Now(()) } // Gee Alex, doesn't this seem like premature optimization. Well you see there // Billy, you're absolutely correct! If your server is *bottlenecked* on // rendering the `Date` header, well then boy do I have news for you, you don't // need this optimization. // // In all seriousness, though, a simple "hello world" benchmark which just sends // back literally "hello world" with standard headers actually is bottlenecked // on rendering a date into a byte buffer. Since it was at the top of a profile, // and this was done for some competitive benchmarks, this module was written. // // Just to be clear, though, I was not intending on doing this because it really // does seem kinda absurd, but it was done by someone else [1], so I blame them! // :) // // [1]: https://github.com/rapidoid/rapidoid/blob/f1c55c0555007e986b5d069fe1086e6d09933f7b/rapidoid-commons/src/main/java/org/rapidoid/commons/Dates.java#L48-L66 struct LastRenderedNow { bytes: [u8; 128], amt: usize, next_update: time::Timespec, } thread_local!(static LAST: RefCell<LastRenderedNow> = RefCell::new(LastRenderedNow { bytes: [0; 128], amt: 0, next_update: time::Timespec::new(0, 0), })); impl fmt::Display for Now { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { LAST.with(|cache| { let mut cache = cache.borrow_mut(); let now = time::get_time(); if now > cache.next_update { cache.update(now); } f.write_str(cache.buffer()) }) } } impl LastRenderedNow { fn buffer(&self) -> &str { str::from_utf8(&self.bytes[..self.amt]).unwrap() } fn update(&mut self, now: time::Timespec) { self.amt = 0; write!(LocalBuffer(self), "{}", time::at(now).rfc822()).unwrap(); self.next_update = now + Duration::seconds(1); self.next_update.nsec = 0; } } struct LocalBuffer<'a>(&'a mut LastRenderedNow); impl<'a> fmt::Write for LocalBuffer<'a> { fn write_str(&mut self, s: &str) -> fmt::Result { let start = self.0.amt; let end = start + s.len(); self.0.bytes[start..end].copy_from_slice(s.as_bytes()); self.0.amt += s.len(); Ok(()) } }