diff --git a/src/servo/gfx.rs b/src/servo/gfx.rs index ce7ab48e535..0a9c458dbf4 100644 --- a/src/servo/gfx.rs +++ b/src/servo/gfx.rs @@ -10,7 +10,11 @@ pub use dl = display_list; pub use display_list::DisplayList; pub use font::Font; -pub use font_cache::FontCache; +pub use font::FontDescriptor; +pub use font::FontGroup; +pub use font::FontSelector; +pub use font::FontStyle; +pub use font::RunMetrics; pub use font_context::FontContext; pub use font_matcher::FontMatcher; pub use geometry::Au; diff --git a/src/servo/gfx/display_list.rs b/src/servo/gfx/display_list.rs index fbdc3ee58a5..ab8ae421f10 100644 --- a/src/servo/gfx/display_list.rs +++ b/src/servo/gfx/display_list.rs @@ -49,7 +49,7 @@ impl DisplayItem { match *self { SolidColor(_, color) => ctx.draw_solid_color(&self.d().bounds, color), Text(_, run, range) => { - let new_run = @run.deserialize(ctx.font_cache); + let new_run = @run.deserialize(ctx.font_ctx); let font = new_run.font; let origin = self.d().bounds.origin; let baseline_origin = Point2D(origin.x, origin.y + font.metrics.ascent); diff --git a/src/servo/gfx/font.rs b/src/servo/gfx/font.rs index b17e8aa0269..a442a0e45f3 100644 --- a/src/servo/gfx/font.rs +++ b/src/servo/gfx/font.rs @@ -38,23 +38,147 @@ enum CSSFontWeight { FontWeight800, FontWeight900, } +pub impl CSSFontWeight : cmp::Eq; -struct FontStyle { +// TODO: eventually this will be split into the specified and used +// font styles. specified contains uninterpreted CSS font property +// values, while 'used' is attached to gfx::Font to descript the +// instance's properties. +// +// For now, the cases are differentiated with a typedef +pub struct FontStyle { pt_size: float, weight: CSSFontWeight, italic: bool, oblique: bool, + families: ~str, + // TODO: font-stretch, text-decoration, font-variant, size-adjust } -struct FontFaceProperties { +// TODO(Issue #181): use deriving for trivial cmp::Eq implementations +pub impl FontStyle : cmp::Eq { + pure fn eq(other: &FontStyle) -> bool { + use std::cmp::FuzzyEq; + + self.pt_size.fuzzy_eq(&other.pt_size) && + self.weight == other.weight && + self.italic == other.italic && + self.oblique == other.oblique && + self.families == other.families + } + pure fn ne(other: &FontStyle) -> bool { !self.eq(other) } +} + +pub type SpecifiedFontStyle = FontStyle; +pub type UsedFontStyle = FontStyle; + +// TODO: move me to layout +struct ResolvedFont { + group: @FontGroup, + style: SpecifiedFontStyle, +} + +// FontDescriptor serializes a specific font and used font style +// options, such as point size. + +// It's used to swizzle/unswizzle gfx::Font instances when +// communicating across tasks, such as the display list between layout +// and render tasks. +pub struct FontDescriptor { + style: UsedFontStyle, + selector: FontSelector, +} + + +// TODO(Issue #181): use deriving for trivial cmp::Eq implementations +pub impl FontDescriptor : cmp::Eq { + pure fn eq(other: &FontDescriptor) -> bool { + self.style == other.style && + self.selector == other.selector + } + pure fn ne(other: &FontDescriptor) -> bool { !self.eq(other) } +} + +pub impl FontDescriptor { + static pure fn new(style: &UsedFontStyle, selector: &FontSelector) -> FontDescriptor { + FontDescriptor { + style: copy *style, + selector: copy *selector, + } + } +} + +// A FontSelector is a platform-specific strategy for serializing face names. +pub enum FontSelector { + SelectorPlatformName(~str), + SelectorStubDummy, // aka, use Josephin Sans +} + +// TODO(Issue #181): use deriving for trivial cmp::Eq implementations +pub impl FontSelector : cmp::Eq { + pure fn eq(other: &FontSelector) -> bool { + match (self, *other) { + (SelectorStubDummy, SelectorStubDummy) => true, + (SelectorPlatformName(a), SelectorPlatformName(b)) => a == b, + _ => false + } + } + pure fn ne(other: &FontSelector) -> bool { !self.eq(other) } +} + +// Holds a specific font family, and the various +pub struct FontFamily { family_name: @str, - face_name: ~str, - priv weight: u16, - priv italic: bool, + entries: ~[@FontEntry], } -impl FontFaceProperties { - pure fn is_bold() -> bool { self.weight >= (500 as u16) } +// This struct is the result of mapping a specified FontStyle into the +// available fonts on the system. It contains an ordered list of font +// instances to be used in case the prior font cannot be used for +// rendering the specified language. + +// The ordering of font instances is mainly decided by the CSS +// 'font-family' property. The last font is a system fallback font. +pub struct FontGroup { + families: @str, + // style of the first western font in group, which is + // used for purposes of calculating text run metrics. + style: UsedFontStyle, + fonts: ~[@Font], +} + +pub impl FontGroup { + static fn new(families: @str, style: &UsedFontStyle, fonts: ~[@Font]) -> FontGroup { + FontGroup { + families: families, + style: copy *style, + fonts: move fonts, + } + } +} + + +// This struct summarizes an available font's features. In the future, +// this will include fiddly settings such as special font table handling. + +// In the common case, each FontFamily will have a singleton FontEntry, or +// it will have the standard four faces: Normal, Bold, Italic, BoldItalic. +struct FontEntry { + family: @FontFamily, + face_name: ~str, + priv weight: CSSFontWeight, + priv italic: bool, + // TODO: array of OpenType features, etc. +} + +impl FontEntry { + pure fn is_bold() -> bool { + match self.weight { + FontWeight900 | FontWeight800 | FontWeight700 | FontWeight600 => true, + _ => false + } + } + pure fn is_italic() -> bool { self.italic } } @@ -69,7 +193,7 @@ struct RunMetrics { } /** -A font handle. Layout can use this to calculate glyph metrics +A font instance. Layout can use this to calculate glyph metrics and the renderer can use it to render text. */ struct Font { @@ -77,7 +201,7 @@ struct Font { priv handle: FontHandle, priv mut azure_font: Option, priv mut shaper: Option<@Shaper>, - style: FontStyle, + style: UsedFontStyle, metrics: FontMetrics, drop { @@ -88,7 +212,7 @@ struct Font { impl Font { // TODO: who should own fontbuf? - static fn new(fontbuf: @~[u8], handle: FontHandle, style: FontStyle) -> Font { + static fn new(fontbuf: @~[u8], handle: FontHandle, style: UsedFontStyle) -> Font { let metrics = handle.get_metrics(); Font { @@ -214,6 +338,7 @@ pub trait FontMethods { fn draw_text_into_context(rctx: &RenderContext, run: &TextRun, range: Range, baseline_origin: Point2D); fn measure_text(&TextRun, Range) -> RunMetrics; fn shape_text(@self, &str) -> GlyphStore; + fn get_descriptor() -> FontDescriptor; fn buf(&self) -> @~[u8]; // these are used to get glyphs and advances in the case that the @@ -315,6 +440,12 @@ pub impl Font : FontMethods { return move store; } + fn get_descriptor() -> FontDescriptor { + // TODO(Issue #174): implement by-platform-name FontSelectors, + // probably by adding such an API to FontHandle. + FontDescriptor::new(&font_context::dummy_style(), &SelectorStubDummy) + } + fn buf(&self) -> @~[u8] { self.fontbuf } diff --git a/src/servo/gfx/font_cache.rs b/src/servo/gfx/font_cache.rs index 7e2c06cf489..5d6593e60a7 100644 --- a/src/servo/gfx/font_cache.rs +++ b/src/servo/gfx/font_cache.rs @@ -1,14 +1,8 @@ +/* use font::{Font, FontStyle, FontWeight300}; use native::{FontHandle, FontContext}; -// TODO(Issue #164): delete, and get default font from font list -const TEST_FONT: [u8 * 33004] = #include_bin("JosefinSans-SemiBold.ttf"); - -fn test_font_bin() -> ~[u8] { - return vec::from_fn(33004, |i| TEST_FONT[i]); -} - -// Dummy font cache. +// Font cache that reuses gfx::Font instances. struct FontCache { fctx: @FontContext, @@ -24,36 +18,19 @@ impl FontCache { } pub fn get_test_font(@self) -> @Font { - let dummy_style = FontStyle { - pt_size: 40f, - weight: FontWeight300, - italic: false, - oblique: false - }; + return match self.cached_font { Some(font) => font, None => match self.get_font(&dummy_style) { Ok(font) => { self.cached_font = Some(font); font } - Err(*) => /* FIXME */ fail + Err(*) => fail } } } - // TODO: maybe FontStyle should be canonicalized when used in FontCache? - priv fn create_font(style: &FontStyle) -> Result<@Font, ()> { - let font_bin = @test_font_bin(); - let native_font = FontHandle::new(self.fctx, font_bin, style.pt_size); - let native_font = if native_font.is_ok() { - result::unwrap(move native_font) - } else { - return Err(native_font.get_err()); - }; - - return Ok(@Font::new(font_bin, move native_font, copy *style)); - } - pub fn get_font(@self, style: &FontStyle) -> Result<@Font, ()> { self.create_font(style) } -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/src/servo/gfx/font_context.rs b/src/servo/gfx/font_context.rs index fbaab9541b9..8a168790c36 100644 --- a/src/servo/gfx/font_context.rs +++ b/src/servo/gfx/font_context.rs @@ -1,3 +1,34 @@ +use dvec::DVec; + +use util::cache; +use gfx::{ + FontDescriptor, + FontSelector, + FontStyle, +}; +use gfx::font::{SelectorPlatformName, SelectorStubDummy, SpecifiedFontStyle}; +use gfx::native::FontHandle; + +// TODO(Issue #164): delete, and get default font from font list +const TEST_FONT: [u8 * 33004] = #include_bin("JosefinSans-SemiBold.ttf"); + +fn test_font_bin() -> ~[u8] { + return vec::from_fn(33004, |i| TEST_FONT[i]); +} + +// TODO(Rust #3934): creating lots of new dummy styles is a workaround +// for not being able to store symbolic enums in top-level constants. +pub fn dummy_style() -> FontStyle { + use gfx::font::FontWeight300; + return FontStyle { + pt_size: 20f, + weight: FontWeight300, + italic: false, + oblique: false, + families: ~"Helvetica, serif", + } +} + // TODO(Issue #163): this is a workaround for static methods and // typedefs not working well together. It should be removed. @@ -5,21 +36,91 @@ // to conditionally define the entire impl. #[cfg(target_os = "macos")] -type FontContext/& = quartz::font_context::QuartzFontContext; +type FontContextHandle/& = quartz::font_context::QuartzFontContext; #[cfg(target_os = "linux")] -type FontContext/& = freetype::font_context::FreeTypeFontContext; +type FontContextHandle/& = freetype::font_context::FreeTypeFontContext; #[cfg(target_os = "macos")] -pub impl FontContext { - static pub fn new() -> FontContext { +pub impl FontContextHandle { + static pub fn new() -> FontContextHandle { quartz::font_context::QuartzFontContext::new() } } #[cfg(target_os = "linux")] -pub impl FontContext { - static pub fn new() -> FontContext { +pub impl FontContextHandle { + static pub fn new() -> FontContextHandle { freetype::font_context::FreeTypeFontContext::new() } } + +pub struct FontContext { + instance_cache: cache::MonoCache, + handle: FontContextHandle, +} + +pub impl FontContext { + static fn new() -> FontContext { + FontContext { + // TODO(Rust #3902): remove extraneous type parameters once they are inferred correctly. + instance_cache: cache::new::>(10), + handle: FontContextHandle::new() + } + } + + fn get_resolved_font_for_style(style: &SpecifiedFontStyle) -> @FontGroup { + // TODO(Issue #178, E): implement a cache of FontGroup instances. + self.create_font_group(style) + } + + fn get_font_by_descriptor(desc: &FontDescriptor) -> Result<@Font, ()> { + match self.instance_cache.find(desc) { + Some(f) => Ok(f), + None => { + let result = self.create_font_instance(desc); + match result { + Ok(font) => { + self.instance_cache.insert(desc, font); + }, _ => {} + }; + result + } + } + } + + priv fn create_font_group(style: &SpecifiedFontStyle) -> @FontGroup { + // TODO(Issue #178, D): implement private font matching + // TODO(Issue #174): implement by-platform-name FontSelectors + let desc = FontDescriptor::new(&font_context::dummy_style(), &SelectorStubDummy); + let fonts = DVec(); + match self.get_font_by_descriptor(&desc) { + Ok(instance) => fonts.push(instance), + Err(()) => {} + } + + assert fonts.len() > 0; + // TODO(Issue #179): Split FontStyle into specified and used styles + let used_style = copy *style; + + @FontGroup::new(style.families.to_managed(), &used_style, dvec::unwrap(move fonts)) + } + + priv fn create_font_instance(desc: &FontDescriptor) -> Result<@Font, ()> { + match desc.selector { + SelectorStubDummy => { + let font_bin = @test_font_bin(); + let handle = FontHandle::new(&self.handle, font_bin, desc.style.pt_size); + let handle = if handle.is_ok() { + result::unwrap(move handle) + } else { + return Err(handle.get_err()); + }; + + return Ok(@Font::new(font_bin, move handle, copy desc.style)); + }, + // TODO(Issue #174): implement by-platform-name font selectors. + SelectorPlatformName(_) => { fail ~"FontContext::create_font_instance() can't yet handle SelectorPlatformName." } + } + } +} \ No newline at end of file diff --git a/src/servo/gfx/font_handle.rs b/src/servo/gfx/font_handle.rs index 0473a5e8755..9966f36975b 100644 --- a/src/servo/gfx/font_handle.rs +++ b/src/servo/gfx/font_handle.rs @@ -20,14 +20,14 @@ pub type FontHandle/& = freetype::font_handle::FreeTypeFontHandle; // to conditionally define the entire impl. #[cfg(target_os = "macos")] impl FontHandle { - static pub fn new(fctx: &native::FontContext, buf: @~[u8], pt_size: float) -> Result { + static pub fn new(fctx: &native::FontContextHandle, buf: @~[u8], pt_size: float) -> Result { quartz::font_handle::QuartzFontHandle::new(fctx, buf, pt_size) } } #[cfg(target_os = "linux")] impl FontHandle { - static pub fn new(fctx: &native::FontContext, buf: @~[u8], pt_size: float) -> Result { + static pub fn new(fctx: &native::FontContextHandle, buf: @~[u8], pt_size: float) -> Result { freetype::font_handle::FreeTypeFontHandle::new(fctx, buf, pt_size) } } \ No newline at end of file diff --git a/src/servo/gfx/harfbuzz/shaper.rs b/src/servo/gfx/harfbuzz/shaper.rs index 9f878f1fe7d..5065e36bcf6 100644 --- a/src/servo/gfx/harfbuzz/shaper.rs +++ b/src/servo/gfx/harfbuzz/shaper.rs @@ -1,11 +1,9 @@ extern mod harfbuzz; +use gfx::au; use gfx::{ - au, - Au, Font, - FontCache, }; use geom::point::Point2D; diff --git a/src/servo/gfx/native.rs b/src/servo/gfx/native.rs index 8d8154425aa..15e69e6de94 100644 --- a/src/servo/gfx/native.rs +++ b/src/servo/gfx/native.rs @@ -5,4 +5,4 @@ Note that you still must define each of the files as a module in servo.rc. This is not ideal and may be changed in the future. */ pub use font_handle::FontHandle; -pub use font_context::FontContext; \ No newline at end of file +pub use font_context::FontContextHandle; \ No newline at end of file diff --git a/src/servo/gfx/render_context.rs b/src/servo/gfx/render_context.rs index 054114bda67..b3f44c39467 100644 --- a/src/servo/gfx/render_context.rs +++ b/src/servo/gfx/render_context.rs @@ -1,12 +1,12 @@ -use au = geometry; use compositor::LayerBuffer; +use gfx::au; use gfx::{ + Au, Font, - FontCache, + FontContext, TextRun, }; use image::base::Image; -use au::Au; use util::range::Range; use cairo::cairo_hl::ImageSurface; @@ -23,7 +23,7 @@ use azure::azure_hl::{DrawTarget, Linear}; struct RenderContext { canvas: &LayerBuffer, - font_cache: @FontCache, + font_ctx: @FontContext, } impl RenderContext { diff --git a/src/servo/gfx/render_task.rs b/src/servo/gfx/render_task.rs index 295be058583..5afb0eb8fb5 100644 --- a/src/servo/gfx/render_task.rs +++ b/src/servo/gfx/render_task.rs @@ -9,7 +9,6 @@ use geom::matrix2d::Matrix2D; use dl = display_list; use gfx::{ - FontCache, FontContext, RenderContext, RenderLayer, @@ -42,7 +41,7 @@ pub fn RenderTask(compositor: C) -> RenderTask { port: po, compositor: move compositor, mut layer_buffer_set_port: Cell(move layer_buffer_set_port), - font_cache: @FontCache::new(@FontContext::new()), + font_ctx: @FontContext::new(), }.start(); } } @@ -51,7 +50,7 @@ priv struct Renderer { port: comm::Port, compositor: C, layer_buffer_set_port: Cell>, - font_cache: @FontCache + font_ctx: @FontContext } impl Renderer { @@ -97,7 +96,7 @@ impl Renderer { |render_layer, layer_buffer| { let ctx = RenderContext { canvas: layer_buffer, - font_cache: self.font_cache + font_ctx: self.font_ctx }; // Apply the translation to render the tile we want. diff --git a/src/servo/gfx/text_run.rs b/src/servo/gfx/text_run.rs index 9a28d38e596..cf271dabc9b 100644 --- a/src/servo/gfx/text_run.rs +++ b/src/servo/gfx/text_run.rs @@ -1,11 +1,15 @@ use arc = std::arc; use arc::ARC; -use au = gfx::geometry; -use font::{RunMetrics, Font}; -use font_cache::FontCache; use geom::point::Point2D; use geom::size::Size2D; -use gfx::geometry::Au; +use gfx::au; +use gfx::{ + Au, + Font, + FontContext, + FontDescriptor, + RunMetrics, +}; use glyph::GlyphStore; use layout::context::LayoutContext; use libc::{c_void}; @@ -23,16 +27,20 @@ pub struct TextRun { // we instead use ARC everywhere. pub struct SendableTextRun { text: ~str, - font_descriptor: (), + font: FontDescriptor, priv glyphs: GlyphStore, } impl SendableTextRun { - pub fn deserialize(&self, cache: @FontCache) -> TextRun { + pub fn deserialize(&self, fctx: @FontContext) -> TextRun { + let font = match fctx.get_font_by_descriptor(&self.font) { + Ok(f) => f, + Err(_) => fail fmt!("Font descriptor deserialization failed! desc=%?", self.font) + }; + TextRun { text: copy self.text, - // TODO: actually deserialize a font descriptor thingy - font: cache.get_test_font(), + font: font, glyphs: copy self.glyphs } } @@ -49,11 +57,10 @@ impl TextRun { return move run; } - pub pure fn serialize(&self, _cache: @FontCache) -> SendableTextRun { + pub fn serialize(&self) -> SendableTextRun { SendableTextRun { text: copy self.text, - // TODO: actually serialize a font descriptor thingy - font_descriptor: (), + font: self.font.get_descriptor(), glyphs: copy self.glyphs, } } diff --git a/src/servo/layout/box.rs b/src/servo/layout/box.rs index 70f5e417fcf..747fc8e8377 100644 --- a/src/servo/layout/box.rs +++ b/src/servo/layout/box.rs @@ -409,9 +409,7 @@ impl RenderBox : RenderBoxMethods { match *self { UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here.", TextBox(_,d) => { - list.append_item(~DisplayItem::new_Text(&abs_box_bounds, - ~d.run.serialize(builder.ctx.font_cache), - d.range)); + list.append_item(~DisplayItem::new_Text(&abs_box_bounds, ~d.run.serialize(), d.range)); // debug frames for text box bounds debug!("%?", { list.append_item(~DisplayItem::new_Border(&abs_box_bounds, au::from_px(1), diff --git a/src/servo/layout/context.rs b/src/servo/layout/context.rs index 0b258c5bab8..972e8c61aa4 100644 --- a/src/servo/layout/context.rs +++ b/src/servo/layout/context.rs @@ -1,7 +1,7 @@ use geom::rect::Rect; use gfx::{ Au, - FontCache, + FontContext, }; use resource::local_image_cache::LocalImageCache; use std::net::url::Url; @@ -9,7 +9,7 @@ use std::net::url::Url; /* Represents layout task context. */ struct LayoutContext { - font_cache: @FontCache, + font_ctx: @FontContext, image_cache: @LocalImageCache, doc_url: Url, screen_size: Rect diff --git a/src/servo/layout/inline.rs b/src/servo/layout/inline.rs index 77cd0433786..a6817360191 100644 --- a/src/servo/layout/inline.rs +++ b/src/servo/layout/inline.rs @@ -7,6 +7,7 @@ use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; use gfx::display_list::{DisplayList, DisplayListBuilder}; +use gfx::font::FontStyle; use gfx::geometry::Au; use layout::box::*; use layout::context::LayoutContext; @@ -235,8 +236,13 @@ impl TextRunScanner { // TODO(Issue #115): use actual CSS 'white-space' property of relevant style. let compression = CompressWhitespaceNewline; let transformed_text = transform_text(text, compression); - // TODO(Issue #116): use actual font for corresponding DOM node to create text run. - let run = @TextRun::new(ctx.font_cache.get_test_font(), move transformed_text); + // TODO(Issue #116): use actual font and style for corresponding + // DOM node to create text run. + // TODO(Issue #177): text run creation must account for text-renderability by fontgroup fonts. + // this is probably achieved by creating fontgroup above, and then letting FontGroup decide + // which Font to stick into the TextRun. + let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&gfx::font_context::dummy_style()); + let run = @TextRun::new(fontgroup.fonts[0], move transformed_text); debug!("TextRunScanner: pushing single text box in range: %?", self.clump); let new_box = layout::text::adapt_textbox_with_range(in_boxes[self.clump.begin()].d(), run, Range(0, run.text.len())); @@ -269,7 +275,11 @@ impl TextRunScanner { // create the run, then make new boxes with the run and adjusted text indices // TODO(Issue #116): use actual font for corresponding DOM node to create text run. - let run = @gfx::TextRun::new(ctx.font_cache.get_test_font(), move run_str); + // TODO(Issue #177): text run creation must account for text-renderability by fontgroup fonts. + // this is probably achieved by creating fontgroup above, and then letting FontGroup decide + // which Font to stick into the TextRun. + let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&gfx::font_context::dummy_style()); + let run = @TextRun::new(fontgroup.fonts[0], move run_str); debug!("TextRunScanner: pushing box(es) in range: %?", self.clump); for self.clump.eachi |i| { let range = new_ranges[i - self.clump.begin()]; diff --git a/src/servo/layout/layout_task.rs b/src/servo/layout/layout_task.rs index 0fad5fd4a10..8ff8357ffff 100644 --- a/src/servo/layout/layout_task.rs +++ b/src/servo/layout/layout_task.rs @@ -11,11 +11,10 @@ use dom::node::{Node, LayoutData}; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; +use gfx::{au, dl}; use gfx::{ - au, dl, Au, DisplayList, - FontCache, FontContext, FontMatcher, RenderLayer, @@ -81,7 +80,7 @@ struct Layout { local_image_cache: @LocalImageCache, from_content: comm::Port, - font_cache: @FontCache, + font_ctx: @FontContext, font_matcher: @FontMatcher, // This is used to root auxilliary RCU reader data layout_refs: DVec<@LayoutData>, @@ -100,7 +99,7 @@ fn Layout(render_task: RenderTask, local_image_cache: @LocalImageCache(move image_cache_task), from_content: from_content, font_matcher: @FontMatcher::new(fctx), - font_cache: @FontCache::new(fctx), + font_ctx: fctx, layout_refs: DVec(), css_select_ctx: Mut(new_css_select_ctx()) } @@ -169,7 +168,7 @@ impl Layout { let layout_ctx = LayoutContext { image_cache: self.local_image_cache, - font_cache: self.font_cache, + font_ctx: self.font_ctx, doc_url: move doc_url, screen_size: Rect(Point2D(Au(0), Au(0)), screen_size) }; diff --git a/src/servo/servo.rc b/src/servo/servo.rc index 694561c408c..0719f8374b0 100755 --- a/src/servo/servo.rc +++ b/src/servo/servo.rc @@ -142,6 +142,7 @@ pub mod resource { pub mod util { pub mod actor; + pub mod cache; pub mod range; pub mod text; pub mod time; diff --git a/src/servo/util/cache.rs b/src/servo/util/cache.rs new file mode 100644 index 00000000000..6c713cb1291 --- /dev/null +++ b/src/servo/util/cache.rs @@ -0,0 +1,59 @@ +use core::cmp::*; + +trait Cache { + static fn new(size: uint) -> self; + fn insert(key: &K, value: V); + fn find(key: &K) -> Option; + fn find_or_create(key: &K, blk: pure fn&(&K) -> V) -> V; + fn evict_all(); +} + +pub struct MonoCache { + mut entry: Option<(K,V)>, +} + +pub impl MonoCache : Cache { + static fn new(_size: uint) -> MonoCache { + MonoCache { entry: None } + } + + fn insert(key: &K, value: V) { + self.entry = Some((copy *key, value)); + } + + fn find(key: &K) -> Option { + match self.entry { + None => None, + Some((ref k,v)) => if *k == *key { Some(v) } else { None } + } + } + + fn find_or_create(key: &K, blk: pure fn&(&K) -> V) -> V { + return match self.find(key) { + None => { + let value = blk(key); + self.entry = Some((copy *key, copy value)); + move value + }, + Some(v) => v + }; + } + fn evict_all() { + self.entry = None; + } +} + +#[test] +fn test_monocache() { + // TODO: this is hideous because of Rust Issue #3902 + let cache = cache::new::>(10); + let one = @"one"; + let two = @"two"; + cache.insert(1, one); + + assert cache.find(1).is_some(); + assert cache.find(2).is_none(); + cache.find_or_create(2, |_v| { two }); + assert cache.find(2).is_some(); + assert cache.find(1).is_none(); +} \ No newline at end of file diff --git a/src/servo/util/text.rs b/src/servo/util/text.rs index 6f39291978e..61bfa4db21f 100644 --- a/src/servo/util/text.rs +++ b/src/servo/util/text.rs @@ -162,7 +162,7 @@ fn test_transform_discard_newline() { ~"foo bar baz", ~"foobarbaz"]; - assert core::vec::same_length(test_strs, oracle_strs); + assert test_strs.len() == oracle_strs.len(); let mode = DiscardNewline; for uint::range(0, test_strs.len()) |i| { @@ -188,7 +188,7 @@ fn test_transform_compress_whitespace() { ~"foo bar baz", ~"foobarbaz\n\n"]; - assert core::vec::same_length(test_strs, oracle_strs); + assert test_strs.len() == oracle_strs.len(); let mode = CompressWhitespace; for uint::range(0, test_strs.len()) |i| { @@ -214,7 +214,7 @@ fn test_transform_compress_whitespace_newline() { ~"foo bar baz", ~"foobarbaz "]; - assert core::vec::same_length(test_strs, oracle_strs); + assert test_strs.len() == oracle_strs.len(); let mode = CompressWhitespaceNewline; for uint::range(0, test_strs.len()) |i| {