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

Add an LRU cache and use it for fonts and font groups #525

Merged
merged 1 commit into from Jun 20, 2013
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -2,11 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use font::{Font, FontDescriptor, FontGroup, FontStyle, SelectorPlatformIdentifier};
use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, FontStyle,
SelectorPlatformIdentifier};
use font::{SpecifiedFontStyle, UsedFontStyle};
use font_list::FontList;
use servo_util::cache::Cache;
use servo_util::cache::MonoCache;
use servo_util::cache::LRUCache;
use servo_util::time::ProfilerChan;

use platform::font::FontHandle;
@@ -35,8 +36,9 @@ pub trait FontContextHandleMethods {

#[allow(non_implicitly_copyable_typarams)]
pub struct FontContext {
instance_cache: MonoCache<FontDescriptor, @mut Font>,
instance_cache: LRUCache<FontDescriptor, @mut Font>,
font_list: Option<FontList>, // only needed by layout
group_cache: LRUCache<SpecifiedFontStyle, @FontGroup>,
handle: FontContextHandle,
backend: BackendType,
generic_fonts: HashMap<~str,~str>,
@@ -63,10 +65,9 @@ pub impl<'self> FontContext {
generic_fonts.insert(~"monospace", ~"Menlo");

FontContext {
// TODO(Rust #3902): remove extraneous type parameters once they are inferred correctly.
instance_cache:
Cache::new::<FontDescriptor,@mut Font,MonoCache<FontDescriptor,@mut Font>>(10),
instance_cache: LRUCache::new(10),
font_list: font_list,
group_cache: LRUCache::new(10),
handle: handle,
backend: backend,
generic_fonts: generic_fonts,
@@ -78,15 +79,29 @@ pub impl<'self> FontContext {
self.font_list.get_ref()
}

fn get_resolved_font_for_style(@mut self, style: &SpecifiedFontStyle) -> @FontGroup {
// TODO(Issue #178, E): implement a cache of FontGroup instances.
self.create_font_group(style)
fn get_resolved_font_for_style(&mut self, style: &SpecifiedFontStyle) -> @FontGroup {
match self.group_cache.find(style) {
Some(fg) => {
debug!("font group cache hit");
fg
},
None => {
debug!("font group cache miss");
let fg = self.create_font_group(style);
self.group_cache.insert(style, fg);
fg
}
}
}

fn get_font_by_descriptor(&mut self, desc: &FontDescriptor) -> Result<@mut Font, ()> {
match self.instance_cache.find(desc) {
Some(f) => Ok(f),
Some(f) => {
debug!("font cache hit");
Ok(f)
},
None => {
debug!("font cache miss");
let result = self.create_font_instance(desc);
match result {
Ok(font) => {
@@ -108,27 +123,34 @@ pub impl<'self> FontContext {
}
}

// TODO:(Issue #196): cache font groups on the font context.
priv fn create_font_group(@mut self, style: &SpecifiedFontStyle) -> @FontGroup {
priv fn create_font_group(&mut self, style: &SpecifiedFontStyle) -> @FontGroup {
let mut fonts = ~[];

debug!("(create font group) --- starting ---");

let list = self.get_font_list();

// TODO(Issue #193): make iteration over 'font-family' more robust.
for str::each_split_char(style.families, ',') |family| {
let family_name = str::trim(family);
let transformed_family_name = self.transform_family(family_name);
debug!("(create font group) transformed family is `%s`", transformed_family_name);

let result = list.find_font_in_family(transformed_family_name, style);
let result = match self.font_list {
Some(ref fl) => {
fl.find_font_in_family(transformed_family_name, style)
},
None => None,
};

let mut found = false;
for result.each |font_entry| {
found = true;
// TODO(Issue #203): route this instantion through FontContext's Font instance cache.
let instance = Font::new_from_existing_handle(self, &font_entry.handle, style, self.backend,
self.profiler_chan.clone());

let font_id =
SelectorPlatformIdentifier(font_entry.handle.face_identifier());
let font_desc = FontDescriptor::new(copy *style, font_id);

let instance = self.get_font_by_descriptor(&font_desc);

do result::iter(&instance) |font: &@mut Font| { fonts.push(*font); }
};

@@ -140,13 +162,20 @@ pub impl<'self> FontContext {
let last_resort = FontList::get_last_resort_font_families();

for last_resort.each |family| {
let result = list.find_font_in_family(*family,style);
let result = match self.font_list {
Some(ref fl) => {
fl.find_font_in_family(*family, style)
},
None => None,
};

for result.each |font_entry| {
let instance = Font::new_from_existing_handle(self,
&font_entry.handle,
style,
self.backend,
self.profiler_chan.clone());
let font_id =
SelectorPlatformIdentifier(font_entry.handle.face_identifier());
let font_desc = FontDescriptor::new(copy *style, font_id);

let instance = self.get_font_by_descriptor(&font_desc);

do result::iter(&instance) |font: &@mut Font| {
fonts.push(*font);
}
@@ -3,9 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

pub trait Cache<K: Copy + Eq, V: Copy> {
fn new(size: uint) -> Self;
fn insert(&mut self, key: &K, value: V);
fn find(&self, key: &K) -> Option<V>;
fn find(&mut self, key: &K) -> Option<V>;
fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V;
fn evict_all(&mut self);
}
@@ -14,16 +13,18 @@ pub struct MonoCache<K, V> {
entry: Option<(K,V)>,
}

impl<K: Copy + Eq, V: Copy> Cache<K,V> for MonoCache<K,V> {
pub impl<K: Copy + Eq, V: Copy> MonoCache<K,V> {
fn new(_size: uint) -> MonoCache<K,V> {
MonoCache { entry: None }
}
}

impl<K: Copy + Eq, V: Copy> Cache<K,V> for MonoCache<K,V> {
fn insert(&mut self, key: &K, value: V) {
self.entry = Some((copy *key, value));
}

fn find(&self, key: &K) -> Option<V> {
fn find(&mut self, key: &K) -> Option<V> {
match self.entry {
None => None,
Some((ref k,v)) => if *k == *key { Some(v) } else { None }
@@ -47,8 +48,7 @@ impl<K: Copy + Eq, V: Copy> Cache<K,V> for MonoCache<K,V> {

#[test]
fn test_monocache() {
// TODO: this is hideous because of Rust Issue #3902
let cache = cache::new::<uint, @str, MonoCache<uint, @str>>(10);
let cache = MonoCache::new(10);
let one = @"one";
let two = @"two";
cache.insert(&1, one);
@@ -59,3 +59,91 @@ fn test_monocache() {
assert!(cache.find(&2).is_some());
assert!(cache.find(&1).is_none());
}

pub struct LRUCache<K, V> {
entries: ~[(K, V)],
cache_size: uint,
}

pub impl<K: Copy + Eq, V: Copy> LRUCache<K,V> {
fn new(size: uint) -> LRUCache<K, V> {
LRUCache {
entries: ~[],
cache_size: size,
}
}

fn touch(&mut self, pos: uint) -> V {
let (key, val) = copy self.entries[pos];
if pos != self.cache_size {
self.entries.remove(pos);
self.entries.push((key, val));
}
val
}
}

impl<K: Copy + Eq, V: Copy> Cache<K,V> for LRUCache<K,V> {
fn insert(&mut self, key: &K, val: V) {
if self.entries.len() == self.cache_size {
self.entries.remove(0);
}
self.entries.push((copy *key, val));
}

fn find(&mut self, key: &K) -> Option<V> {
match self.entries.position(|&(k, _)| k == *key) {
Some(pos) => Some(self.touch(pos)),
None => None,
}
}

fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V {
match self.entries.position(|&(k, _)| k == *key) {
Some(pos) => self.touch(pos),
None => {
let val = blk(key);
self.insert(key, val);
val
}
}
}

fn evict_all(&mut self) {
self.entries.clear();
}
}

#[test]
fn test_lru_cache() {
let one = @"one";
let two = @"two";
let three = @"three";
let four = @"four";

// Test normal insertion.
let cache = LRUCache::new(2); // (_, _) (cache is empty)
cache.insert(&1, one); // (1, _)
cache.insert(&2, two); // (1, 2)
cache.insert(&3, three); // (2, 3)

assert!(cache.find(&1).is_none()); // (2, 3) (no change)
assert!(cache.find(&3).is_some()); // (2, 3)
assert!(cache.find(&2).is_some()); // (3, 2)

// Test that LRU works (this insertion should replace 3, not 2).
cache.insert(&4, four); // (2, 4)

assert!(cache.find(&1).is_none()); // (2, 4) (no change)
assert!(cache.find(&2).is_some()); // (4, 2)
assert!(cache.find(&3).is_none()); // (4, 2) (no change)
assert!(cache.find(&4).is_some()); // (2, 4) (no change)

// Test find_or_create.
do cache.find_or_create(&1) |_| { one } // (4, 1)

assert!(cache.find(&1).is_some()); // (4, 1) (no change)
assert!(cache.find(&2).is_none()); // (4, 1) (no change)
assert!(cache.find(&3).is_none()); // (4, 1) (no change)
assert!(cache.find(&4).is_some()); // (1, 4)
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.