<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use base::ShareContext;
use context::GraphicsContextMethods;

use extra::arc::Arc;
use geom::size::Size2D;
use io_surface::{IOSurface, kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow};
use io_surface::{kIOSurfaceHeight, kIOSurfaceIsGlobal, kIOSurfaceWidth};
use io_surface::IOSurfaceMethods;
use opengles::cgl::{CGLChoosePixelFormat, CGLContextObj, CGLCreateContext};
use opengles::cgl::{CGLSetCurrentContext, CGLTexImageIOSurface2D, kCGLNoError, kCGLPFACompliant};
use opengles::cgl::{kCGLPFADoubleBuffer};
use opengles::gl2::{BGRA, CLAMP_TO_EDGE, COLOR_ATTACHMENT0, FRAMEBUFFER};
use opengles::gl2::{FRAMEBUFFER_COMPLETE, GLenum, GLint, GLsizei, GLuint, LINEAR};
use opengles::gl2::{NEAREST, RGBA, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER};
use opengles::gl2::{TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, TEXTURE_WRAP_T};
use opengles::gl2::{UNSIGNED_INT_8_8_8_8_REV};
use opengles::gl2;
use std::cast::transmute;
use std::ptr::{null, to_unsafe_ptr};

// FIXME: This is not good.
#[link_args="-framework IOSurface -framework CoreFoundation"]
#[nolink]
extern {}

/// Mac-specific interface to 3D graphics contexts.
pub struct GraphicsContext {
    cgl_context: Arc<CGLContextObj>,
}

impl GraphicsContext {
    /// Returns a new context, possibly shared with another context.
    fn new_possibly_shared(share_context: Option<GraphicsContext>) -> GraphicsContext {
        unsafe {
            // Choose a pixel format.
            let attributes = [ kCGLPFADoubleBuffer, kCGLPFACompliant, 0 ];
            let pixel_format = null();
            let pixel_format_count = 1;
            let gl_error = CGLChoosePixelFormat(transmute(&attributes[0]),
                                                to_unsafe_ptr(&pixel_format),
                                                to_unsafe_ptr(&pixel_format_count));
            assert!(gl_error == kCGLNoError);

            // Create the context.
            let cgl_context = null();
            let gl_error = match share_context {
                None => CGLCreateContext(pixel_format, null(), &cgl_context),
                Some(ref share_context) => {
                    let native = share_context.native();
                    CGLCreateContext(pixel_format, *native.get(), &cgl_context)
                }
            };
            assert!(gl_error == kCGLNoError);

            GraphicsContextMethods::wrap(Arc::new(cgl_context))
        }
    }
}

impl GraphicsContextMethods<CGLContextObj> for GraphicsContext {
    /// Wraps the given instance of the native Core OpenGL graphics context.
    fn wrap(instance: Arc<CGLContextObj>) -> GraphicsContext {
        unsafe {
            GraphicsContext {
                cgl_context: instance
            }
        }
    }

    /// Returns the underlying native 3D context without modifying its reference count.
    fn native(&self) -> Arc<CGLContextObj> {
        self.cgl_context.clone()
    }

    /// Creates a new offscreen 3D graphics context.
    fn new() -> GraphicsContext {
        GraphicsContext::new_possibly_shared(None)
    }

    /// Creates a new offscreen 3D graphics context shared with the given context.
    fn new_shared(share_context: GraphicsContext) -> GraphicsContext {
        GraphicsContext::new_possibly_shared(Some(share_context))
    }

    /// Makes this context the current context.
    fn make_current(&self) {
        unsafe {
            let gl_error = CGLSetCurrentContext(*self.cgl_context.get());
            assert!(gl_error == kCGLNoError)
        }
    }
}

impl Clone for GraphicsContext {
    fn clone(&self) -> GraphicsContext {
        GraphicsContextMethods::wrap(self.native())
    }
}

pub struct Context {
    surface: IOSurface,
    framebuffer: GLuint,
    texture: GLuint

    // FIXME: Needs drop.
}

pub fn init_cgl() -> GraphicsContext {
    let context: GraphicsContext = GraphicsContextMethods::new();
    context.make_current();
    context
}

pub fn init_surface(size: Size2D<int>) -> IOSurface {
    use core_foundation::boolean::CFBoolean;
    use core_foundation::number::CFNumber;
    use core_foundation::string::CFString;
    use core_foundation;
    use io_surface;

    // TODO: dictionary constructor should be less ridiculous.
    // Or, we could add bindings for mutable dictionaries.
    let k_width = CFString::wrap_shared(kIOSurfaceWidth);
    let v_width = CFNumber::new(size.width as i32);

    let k_height = CFString::wrap_shared(kIOSurfaceHeight);
    let v_height = CFNumber::new(size.height as i32);

    let k_bytes_per_row = CFString::wrap_shared(kIOSurfaceBytesPerRow);
    let v_bytes_per_row = CFNumber::new(size.width as i32 * 4);

    let k_bytes_per_elem = CFString::wrap_shared(kIOSurfaceBytesPerElement);
    let v_bytes_per_elem = CFNumber::new(4i32);

    let k_is_global = CFString::wrap_shared(kIOSurfaceIsGlobal);
    let v_is_global = CFBoolean::true_value();

    io_surface::new(&core_foundation::dictionary::CFDictionary::new([
        (*k_width.contents.borrow_ref(),          *v_width.contents.borrow_type_ref()),
        (*k_height.contents.borrow_ref(),         *v_height.contents.borrow_type_ref()),
        (*k_bytes_per_row.contents.borrow_ref(),  *v_bytes_per_row.contents.borrow_type_ref()),
        (*k_bytes_per_elem.contents.borrow_ref(), *v_bytes_per_elem.contents.borrow_type_ref()),
        (*k_is_global.contents.borrow_ref(),      *v_is_global.contents.borrow_type_ref()),
    ]))
}

pub fn init_texture() -> GLuint {
    gl2::enable(TEXTURE_RECTANGLE_ARB);

    let texture = gl2::gen_textures(1)[0];
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, texture);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_T, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MAG_FILTER, LINEAR as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MIN_FILTER, NEAREST as GLint);
    return texture;
}

// Assumes the texture is already bound via gl2::bind_texture().
pub fn bind_surface_to_texture(context: &GraphicsContext, surface: &IOSurface, size: Size2D<int>) {
    // FIXME: There should be safe wrappers for this.
    unsafe {
        let native = context.native();
        let gl_error = CGLTexImageIOSurface2D(*native.get(),
                                              TEXTURE_RECTANGLE_ARB,
                                              RGBA as GLenum,
                                              size.width as GLsizei,
                                              size.height as GLsizei,
                                              BGRA as GLenum,
                                              UNSIGNED_INT_8_8_8_8_REV,
                                              transmute(surface.obj.clone()),
                                              0);
        assert!(gl_error == kCGLNoError);
    }
}

pub fn bind_texture_to_framebuffer(texture: GLuint) {
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, 0);
    gl2::framebuffer_texture_2d(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_RECTANGLE_ARB, texture, 0);
    assert!(gl2::check_framebuffer_status(FRAMEBUFFER) == FRAMEBUFFER_COMPLETE);
}

impl ShareContext for Context {
    fn new(size: Size2D<int>) -> Context {
        // Initialize CGL.
        let context = init_cgl();

        // Create the surface.
        let surface = init_surface(size.clone());

        // Create a framebuffer.
        let framebuffer = gl2::gen_framebuffers(1)[0];
        gl2::bind_framebuffer(FRAMEBUFFER, framebuffer);

        // Create and bind to the texture.
        let texture = init_texture();
        bind_surface_to_texture(&context, &surface, size);

        // Bind the texture to the framebuffer.
        bind_texture_to_framebuffer(texture);

        Context {
            surface: surface,
            framebuffer: framebuffer,
            texture: texture
        }
    }

    fn flush(&self) {
        gl2::finish();
    }

    fn id(&self) -> int {
        self.surface.get_id() as int
    }
}

<commit_msg>Warning police.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use base::ShareContext;
use context::GraphicsContextMethods;

use extra::arc::Arc;
use geom::size::Size2D;
use io_surface::{IOSurface, kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow};
use io_surface::{kIOSurfaceHeight, kIOSurfaceIsGlobal, kIOSurfaceWidth};
use io_surface::IOSurfaceMethods;
use opengles::cgl::{CGLChoosePixelFormat, CGLContextObj, CGLCreateContext};
use opengles::cgl::{CGLSetCurrentContext, CGLTexImageIOSurface2D, kCGLNoError, kCGLPFACompliant};
use opengles::cgl::{kCGLPFADoubleBuffer};
use opengles::gl2::{BGRA, CLAMP_TO_EDGE, COLOR_ATTACHMENT0, FRAMEBUFFER};
use opengles::gl2::{FRAMEBUFFER_COMPLETE, GLenum, GLint, GLsizei, GLuint, LINEAR};
use opengles::gl2::{NEAREST, RGBA, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER};
use opengles::gl2::{TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, TEXTURE_WRAP_T};
use opengles::gl2::{UNSIGNED_INT_8_8_8_8_REV};
use opengles::gl2;
use std::cast::transmute;
use std::ptr::{null, to_unsafe_ptr};

// FIXME: This is not good.
#[link_args="-framework IOSurface -framework CoreFoundation"]
#[nolink]
extern {}

/// Mac-specific interface to 3D graphics contexts.
pub struct GraphicsContext {
    cgl_context: Arc<CGLContextObj>,
}

impl GraphicsContext {
    /// Returns a new context, possibly shared with another context.
    fn new_possibly_shared(share_context: Option<GraphicsContext>) -> GraphicsContext {
        unsafe {
            // Choose a pixel format.
            let attributes = [ kCGLPFADoubleBuffer, kCGLPFACompliant, 0 ];
            let pixel_format = null();
            let pixel_format_count = 1;
            let gl_error = CGLChoosePixelFormat(transmute(&attributes[0]),
                                                to_unsafe_ptr(&pixel_format),
                                                to_unsafe_ptr(&pixel_format_count));
            assert!(gl_error == kCGLNoError);

            // Create the context.
            let cgl_context = null();
            let gl_error = match share_context {
                None => CGLCreateContext(pixel_format, null(), &cgl_context),
                Some(ref share_context) => {
                    let native = share_context.native();
                    CGLCreateContext(pixel_format, *native.get(), &cgl_context)
                }
            };
            assert!(gl_error == kCGLNoError);

            GraphicsContextMethods::wrap(Arc::new(cgl_context))
        }
    }
}

impl GraphicsContextMethods<CGLContextObj> for GraphicsContext {
    /// Wraps the given instance of the native Core OpenGL graphics context.
    fn wrap(instance: Arc<CGLContextObj>) -> GraphicsContext {
        GraphicsContext {
            cgl_context: instance
        }
    }

    /// Returns the underlying native 3D context without modifying its reference count.
    fn native(&self) -> Arc<CGLContextObj> {
        self.cgl_context.clone()
    }

    /// Creates a new offscreen 3D graphics context.
    fn new() -> GraphicsContext {
        GraphicsContext::new_possibly_shared(None)
    }

    /// Creates a new offscreen 3D graphics context shared with the given context.
    fn new_shared(share_context: GraphicsContext) -> GraphicsContext {
        GraphicsContext::new_possibly_shared(Some(share_context))
    }

    /// Makes this context the current context.
    fn make_current(&self) {
        unsafe {
            let gl_error = CGLSetCurrentContext(*self.cgl_context.get());
            assert!(gl_error == kCGLNoError)
        }
    }
}

impl Clone for GraphicsContext {
    fn clone(&self) -> GraphicsContext {
        GraphicsContextMethods::wrap(self.native())
    }
}

pub struct Context {
    surface: IOSurface,
    framebuffer: GLuint,
    texture: GLuint

    // FIXME: Needs drop.
}

pub fn init_cgl() -> GraphicsContext {
    let context: GraphicsContext = GraphicsContextMethods::new();
    context.make_current();
    context
}

pub fn init_surface(size: Size2D<int>) -> IOSurface {
    use core_foundation::boolean::CFBoolean;
    use core_foundation::number::CFNumber;
    use core_foundation::string::CFString;
    use core_foundation;
    use io_surface;

    // TODO: dictionary constructor should be less ridiculous.
    // Or, we could add bindings for mutable dictionaries.
    let k_width = CFString::wrap_shared(kIOSurfaceWidth);
    let v_width = CFNumber::new(size.width as i32);

    let k_height = CFString::wrap_shared(kIOSurfaceHeight);
    let v_height = CFNumber::new(size.height as i32);

    let k_bytes_per_row = CFString::wrap_shared(kIOSurfaceBytesPerRow);
    let v_bytes_per_row = CFNumber::new(size.width as i32 * 4);

    let k_bytes_per_elem = CFString::wrap_shared(kIOSurfaceBytesPerElement);
    let v_bytes_per_elem = CFNumber::new(4i32);

    let k_is_global = CFString::wrap_shared(kIOSurfaceIsGlobal);
    let v_is_global = CFBoolean::true_value();

    io_surface::new(&core_foundation::dictionary::CFDictionary::new([
        (*k_width.contents.borrow_ref(),          *v_width.contents.borrow_type_ref()),
        (*k_height.contents.borrow_ref(),         *v_height.contents.borrow_type_ref()),
        (*k_bytes_per_row.contents.borrow_ref(),  *v_bytes_per_row.contents.borrow_type_ref()),
        (*k_bytes_per_elem.contents.borrow_ref(), *v_bytes_per_elem.contents.borrow_type_ref()),
        (*k_is_global.contents.borrow_ref(),      *v_is_global.contents.borrow_type_ref()),
    ]))
}

pub fn init_texture() -> GLuint {
    gl2::enable(TEXTURE_RECTANGLE_ARB);

    let texture = gl2::gen_textures(1)[0];
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, texture);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_T, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MAG_FILTER, LINEAR as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MIN_FILTER, NEAREST as GLint);
    return texture;
}

// Assumes the texture is already bound via gl2::bind_texture().
pub fn bind_surface_to_texture(context: &GraphicsContext, surface: &IOSurface, size: Size2D<int>) {
    // FIXME: There should be safe wrappers for this.
    unsafe {
        let native = context.native();
        let gl_error = CGLTexImageIOSurface2D(*native.get(),
                                              TEXTURE_RECTANGLE_ARB,
                                              RGBA as GLenum,
                                              size.width as GLsizei,
                                              size.height as GLsizei,
                                              BGRA as GLenum,
                                              UNSIGNED_INT_8_8_8_8_REV,
                                              transmute(surface.obj.clone()),
                                              0);
        assert!(gl_error == kCGLNoError);
    }
}

pub fn bind_texture_to_framebuffer(texture: GLuint) {
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, 0);
    gl2::framebuffer_texture_2d(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_RECTANGLE_ARB, texture, 0);
    assert!(gl2::check_framebuffer_status(FRAMEBUFFER) == FRAMEBUFFER_COMPLETE);
}

impl ShareContext for Context {
    fn new(size: Size2D<int>) -> Context {
        // Initialize CGL.
        let context = init_cgl();

        // Create the surface.
        let surface = init_surface(size.clone());

        // Create a framebuffer.
        let framebuffer = gl2::gen_framebuffers(1)[0];
        gl2::bind_framebuffer(FRAMEBUFFER, framebuffer);

        // Create and bind to the texture.
        let texture = init_texture();
        bind_surface_to_texture(&context, &surface, size);

        // Bind the texture to the framebuffer.
        bind_texture_to_framebuffer(texture);

        Context {
            surface: surface,
            framebuffer: framebuffer,
            texture: texture
        }
    }

    fn flush(&self) {
        gl2::finish();
    }

    fn id(&self) -> int {
        self.surface.get_id() as int
    }
}

<|endoftext|>


<commit_before>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

//! # gl_generator
//!
//! `gl_generator` is an OpenGL bindings generator plugin. It defines a macro
//!  named `generate_gl_bindings!` which can be used to generate all constants
//!  and functions of a given OpenGL version.
//!
//! ## Example
//!
//! ~~~rust
//! #[phase(plugin)]
//! extern crate gl_generator;
//! extern crate libc;
//!
//! use std::mem;
//! use self::types::*;
//!
//! generate_gl_bindings! {
//!     api: gl,
//!     profile: core,
//!     version: 4.5,
//!     generator: global,
//!     extensions: [
//!         GL_EXT_texture_filter_anisotropic,
//!     ],
//! }
//! ~~~
//!
//! ## Arguments
//!
//! Each field can be specified at most once, or not at all. If the field is not
//! specified, then a default value will be used.
//!
//! - `api`: The API to generate. Can be either `"gl"`, `"gles1"`, `"gles2"`,
//!   `"wgl"`, `"glx"`, `"egl"`. Defaults to `"gl"`.
//! - `profile`: Can be either `"core"` or `"compatibility"`. Defaults to
//!   `"core"`. `"core"` will only include all functions supported by the
//!   requested version it self, while `"compatibility"` will include all the
//!   functions from previous versions as well.
//! - `version`: The requested API version. This is usually in the form
//!   `"major.minor"`. Defaults to `"1.0"`
//! - `generator`: The type of loader to generate. Can be either `"static"`,
//!   `"global"`, or `"struct"`. Defaults to `"static"`.
//! - `extensions`: Extra extensions to include in the bindings. These are
//!   specified as a list of strings. Defaults to `[]`.
//!
//! ## About EGL
//!
//! When you generate bindings for EGL, the following platform-specific types must be declared
//!  *at the same level where you call `generate_gl_bindings`*:
//!
//! - `khronos_utime_nanoseconds_t`
//! - `khronos_uint64_t`
//! - `khronos_ssize_t`
//! - `EGLNativeDisplayType`
//! - `EGLNativePixmapType`
//! - `EGLNativeWindowType`
//! - `EGLint`
//! - `NativeDisplayType`
//! - `NativePixmapType`
//! - `NativeWindowType`
//!

#![crate_name = "gl_generator"]
#![comment = "OpenGL function loader generator."]
#![license = "ASL2"]
#![crate_type = "dylib"]

#![feature(advanced_slice_patterns)]
#![feature(macro_rules)]
#![feature(phase)]
#![feature(plugin_registrar)]
#![feature(quote)]

#[phase(plugin, link)]
extern crate log;

#[phase(plugin)]
extern crate regex_macros;

extern crate khronos_api;
extern crate regex;
extern crate rustc;
extern crate syntax;

use generators::Generator;
use registry::{Registry, Filter, Ns};
use syntax::ast::{TokenTree, TtDelimited, TtToken};
use syntax::codemap::Span;
use syntax::ext::base::{DummyResult, ExtCtxt, MacResult, MacItems};
use syntax::parse::token;

mod generators;

#[allow(dead_code)]
mod registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut ::rustc::plugin::Registry) {
    reg.register_macro("generate_gl_bindings", macro_handler);
}

/// A predicate that is useful for splitting a comma separated list of tokens
fn is_comma(tt: &TokenTree) -> bool {
    match *tt {
        TtToken(_, token::COMMA) => true,
        _ => false,
    }
}

/// Drops a trailing comma if it exists
fn drop_trailing_comma(tts: &[TokenTree]) -> &[TokenTree] {
    match tts {
        [tts.., TtToken(_, token::COMMA)] => tts,
        tts => tts,
    }
}

/// handler for generate_gl_bindings!
fn macro_handler(ecx: &mut ExtCtxt, span: Span, tts: &[TokenTree]) -> Box<MacResult+'static> {
    // Generator options
    let mut api = None::<(Ns, &'static [u8])>;
    let mut profile = None::<String>;
    let mut version = None::<String>;
    let mut generator = None::<Box<Generator>>;
    let mut extensions = None::<Vec<String>>;

    let tts = drop_trailing_comma(tts);

    // Iterate through the comma separated
    for tts in tts.split(is_comma) {
        let mut it = tts.iter();
        let field = match it.next() {
            Some(&TtToken(_, token::IDENT(ref field, _))) => field.as_str(),
            tt => {
                let span = tt.map(|tt| tt.get_span()).unwrap_or(span);
                ecx.span_err(span, "Expected a generator argument name, \
                                    either: `api`, `profile`, `version`, \
                                    `generator`, or `extensions`.");
                return DummyResult::any(span);
            },
        };
        match it.next() {
            Some(&TtToken(_, token::COLON)) => {},
            tt => {
                let span = tt.map(|tt| tt.get_span()).unwrap_or(span);
                ecx.span_err(span, "Expected `:`");
                return DummyResult::any(span);
            },
        }
        match (field, it.collect::<Vec<_>>().as_slice()) {
            ("api", tts) => {
                if api.is_some() {
                    let span = tts.head().map_or(span, |tt| tt.get_span());
                    ecx.span_err(span, "An API was already specified.");
                    return DummyResult::any(span);
                }
                api = Some(match tts {
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "gl"
                        => (registry::Gl, khronos_api::GL_XML),
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "glx"
                        => (registry::Glx, khronos_api::GLX_XML),
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "wgl"
                        => (registry::Wgl, khronos_api::WGL_XML),
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "egl"
                        => (registry::Egl, khronos_api::EGL_XML),
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "gles1"
                        => (registry::Gles1, khronos_api::GL_XML),
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "gles2"
                        => (registry::Gles2, khronos_api::GL_XML),
                    [&TtToken(span, token::LIT_STR(api))] => {
                        ecx.span_err(span, format!("Unknown API \"{}\"", api.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| tt.get_span());
                        ecx.span_err(span, "Invalid API format, expected \
                                            string.");
                        return DummyResult::any(span);
                    }
                })
            }
            ("profile", tts) => {
                if profile.is_some() {
                    ecx.span_err(tts[0].get_span(), "A profile was already specified.");
                    return DummyResult::any(span);
                }
                profile = Some(match tts {
                    [&TtToken(_, token::LIT_STR(profile))] if profile.as_str() == "core"
                        => "core".to_string(),
                    [&TtToken(_, token::LIT_STR(profile))] if profile.as_str() == "compatibility"
                        => "compatibility".to_string(),
                    [&TtToken(_, token::LIT_STR(profile))] => {
                        let span = tts.head().map_or(span, |tt| tt.get_span());
                        ecx.span_err(span, format!("Unknown profile \"{}\"",
                                                   profile.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| tt.get_span());
                        ecx.span_err(span, "Invalid profile format, expected \
                                            string.");
                        return DummyResult::any(span);
                    },
                })
            }
            ("version", tts) => {
                if version.is_some() {
                    let span = tts.head().map_or(span, |tt| tt.get_span());
                    ecx.span_err(span, "A version was already specified.");
                    return DummyResult::any(span);
                }
                version = Some(match tts {
                    [&TtToken(_, token::LIT_STR(version))] => {
                        version.as_str().to_string()
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| tt.get_span());
                        ecx.span_err(span, "Invalid version format, expected \
                                            string.");
                        return DummyResult::any(span);
                    },
                });
            }
            ("generator", tts) => {
                if generator.is_some() {
                    let span = tts.head().map_or(span, |tt| tt.get_span());
                    ecx.span_err(span, "A generator was already specified.");
                    return DummyResult::any(span);
                }
                generator = Some(match tts {
                    [&TtToken(_, token::LIT_STR(gen))] if gen.as_str() == "global"
                        => box generators::global_gen::GlobalGenerator as Box<Generator>,
                    [&TtToken(_, token::LIT_STR(gen))] if gen.as_str() == "struct"
                        => box generators::struct_gen::StructGenerator as Box<Generator>,
                    [&TtToken(_, token::LIT_STR(gen))] if gen.as_str() == "static"
                        => box generators::static_gen::StaticGenerator as Box<Generator>,
                    [&TtToken(span, token::LIT_STR(gen))] => {
                        ecx.span_err(span, format!("Unknown generator \"{}\"",
                                                   gen.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| tt.get_span());
                        ecx.span_err(span, "Invalid generator format, expected \
                                            string.");
                        return DummyResult::any(span);
                    },
                });
            }
            ("extensions", tts) => {
                if extensions.is_some() {
                    let span = tts.head().map_or(span, |tt| tt.get_span());
                    ecx.span_err(span, "The list of extensions were already \
                                        specified.");
                    return DummyResult::any(span);
                }
                extensions = Some(match tts {
                    [&TtDelimited(span, ref delimited)] => {
                        let (ref begin, ref tts, ref close) = **delimited;
                        if begin.token == token::LBRACKET && close.token ==
                            token::RBRACKET {
                                // Drop the trailing comma if it exists
                                let tts = drop_trailing_comma(tts.as_slice());

                                // Collect the extensions, breaking early if a parse
                                // error occurs.
                                let mut failed = false;
                                let exts = tts.split(is_comma)
                                            .scan((), |_, tts| match tts {
                                    [TtToken(_, token::LIT_STR(ext))] => {
                                        Some(ext.as_str().to_string())
                                    },
                                    _ => {
                                        failed = true;
                                        None
                                    },
                                }).collect();

                                // Cause an error if there is still some leftover
                                // tokens.
                                if failed {
                                    ecx.span_err(span, "Invalid extension format, \
                                                        expected string.");
                                    return DummyResult::any(span);
                                } else {
                                    exts
                                }
                        } else {
                                ecx.span_err(span, "Expected a comma separated \
                                                    list of extension strings \
                                                    delimited by square brackets: \
                                                    `[]`");
                                return DummyResult::any(span);
                        }
                    },
                    _ => {
                        ecx.span_err(span, "Expected a comma separated list of \
                                            extension strings.");
                        return DummyResult::any(span);
                    },
                });
            }
            (field, _) => {
                ecx.span_err(span, format!("Unknown field `{}`", field).as_slice());
                return DummyResult::any(span);
            }
        }
    }

    // Use default values if the parameters have not been set
    let (ns, source) = api.unwrap_or((registry::Gl, khronos_api::GL_XML));
    let extensions = extensions.unwrap_or(vec![]);
    let version = version.unwrap_or("1.0".to_string());
    let generator = generator.unwrap_or(box generators::static_gen::StaticGenerator);
    let profile = profile.unwrap_or("core".to_string());

    // Get generator field values, using default values if they have not been
    // specified
    let filter = Some(Filter {
        api: ns.to_string(),
        extensions: extensions,
        version: version,
        profile: profile,
    });

    // Generate the registry of all bindings
    let registry = {
        use std::io::BufReader;
        use std::task;

        let result = task::try(proc() {
            let reader = BufReader::new(source);
            Registry::from_xml(reader, ns, filter)
        });

        match result {
            Ok(reg) => reg,
            Err(err) => {
                use std::any::{Any, AnyRefExt};
                let err: &Any = &*err;

                match err {
                    err if err.is::<String>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<String>().unwrap().as_slice());
                    },
                    err if err.is::<&'static str>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<&'static str>().unwrap().as_slice());
                    },
                    _ => {
                        ecx.span_err(span, "unknown error while parsing the registry");
                    }
                }

                return DummyResult::any(span);
            }
        }
    };

    // generating the Rust bindings as a source code into "buffer"
    let items = generator.write(ecx, &registry, ns);

    MacItems::new(items.into_iter())
}
<commit_msg>Clean up delimiter matching<commit_after>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

//! # gl_generator
//!
//! `gl_generator` is an OpenGL bindings generator plugin. It defines a macro
//!  named `generate_gl_bindings!` which can be used to generate all constants
//!  and functions of a given OpenGL version.
//!
//! ## Example
//!
//! ~~~rust
//! #[phase(plugin)]
//! extern crate gl_generator;
//! extern crate libc;
//!
//! use std::mem;
//! use self::types::*;
//!
//! generate_gl_bindings! {
//!     api: gl,
//!     profile: core,
//!     version: 4.5,
//!     generator: global,
//!     extensions: [
//!         GL_EXT_texture_filter_anisotropic,
//!     ],
//! }
//! ~~~
//!
//! ## Arguments
//!
//! Each field can be specified at most once, or not at all. If the field is not
//! specified, then a default value will be used.
//!
//! - `api`: The API to generate. Can be either `"gl"`, `"gles1"`, `"gles2"`,
//!   `"wgl"`, `"glx"`, `"egl"`. Defaults to `"gl"`.
//! - `profile`: Can be either `"core"` or `"compatibility"`. Defaults to
//!   `"core"`. `"core"` will only include all functions supported by the
//!   requested version it self, while `"compatibility"` will include all the
//!   functions from previous versions as well.
//! - `version`: The requested API version. This is usually in the form
//!   `"major.minor"`. Defaults to `"1.0"`
//! - `generator`: The type of loader to generate. Can be either `"static"`,
//!   `"global"`, or `"struct"`. Defaults to `"static"`.
//! - `extensions`: Extra extensions to include in the bindings. These are
//!   specified as a list of strings. Defaults to `[]`.
//!
//! ## About EGL
//!
//! When you generate bindings for EGL, the following platform-specific types must be declared
//!  *at the same level where you call `generate_gl_bindings`*:
//!
//! - `khronos_utime_nanoseconds_t`
//! - `khronos_uint64_t`
//! - `khronos_ssize_t`
//! - `EGLNativeDisplayType`
//! - `EGLNativePixmapType`
//! - `EGLNativeWindowType`
//! - `EGLint`
//! - `NativeDisplayType`
//! - `NativePixmapType`
//! - `NativeWindowType`
//!

#![crate_name = "gl_generator"]
#![comment = "OpenGL function loader generator."]
#![license = "ASL2"]
#![crate_type = "dylib"]

#![feature(advanced_slice_patterns)]
#![feature(macro_rules)]
#![feature(phase)]
#![feature(plugin_registrar)]
#![feature(quote)]

#[phase(plugin, link)]
extern crate log;

#[phase(plugin)]
extern crate regex_macros;

extern crate khronos_api;
extern crate regex;
extern crate rustc;
extern crate syntax;

use generators::Generator;
use registry::{Registry, Filter, Ns};
use syntax::ast::{TokenTree, TtDelimited, TtToken};
use syntax::codemap::Span;
use syntax::ext::base::{DummyResult, ExtCtxt, MacResult, MacItems};
use syntax::parse::token;

mod generators;

#[allow(dead_code)]
mod registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut ::rustc::plugin::Registry) {
    reg.register_macro("generate_gl_bindings", macro_handler);
}

/// A predicate that is useful for splitting a comma separated list of tokens
fn is_comma(tt: &TokenTree) -> bool {
    match *tt {
        TtToken(_, token::COMMA) => true,
        _ => false,
    }
}

/// Drops a trailing comma if it exists
fn drop_trailing_comma(tts: &[TokenTree]) -> &[TokenTree] {
    match tts {
        [tts.., TtToken(_, token::COMMA)] => tts,
        tts => tts,
    }
}

/// handler for generate_gl_bindings!
fn macro_handler(ecx: &mut ExtCtxt, span: Span, tts: &[TokenTree]) -> Box<MacResult+'static> {
    // Generator options
    let mut api = None::<(Ns, &'static [u8])>;
    let mut profile = None::<String>;
    let mut version = None::<String>;
    let mut generator = None::<Box<Generator>>;
    let mut extensions = None::<Vec<String>>;

    let tts = drop_trailing_comma(tts);

    // Iterate through the comma separated
    for tts in tts.split(is_comma) {
        let mut it = tts.iter();
        let field = match it.next() {
            Some(&TtToken(_, token::IDENT(ref field, _))) => field.as_str(),
            tt => {
                let span = tt.map(|tt| tt.get_span()).unwrap_or(span);
                ecx.span_err(span, "Expected a generator argument name, \
                                    either: `api`, `profile`, `version`, \
                                    `generator`, or `extensions`.");
                return DummyResult::any(span);
            },
        };
        match it.next() {
            Some(&TtToken(_, token::COLON)) => {},
            tt => {
                let span = tt.map(|tt| tt.get_span()).unwrap_or(span);
                ecx.span_err(span, "Expected `:`");
                return DummyResult::any(span);
            },
        }
        match (field, it.collect::<Vec<_>>().as_slice()) {
            ("api", tts) => {
                if api.is_some() {
                    let span = tts.head().map_or(span, |tt| tt.get_span());
                    ecx.span_err(span, "An API was already specified.");
                    return DummyResult::any(span);
                }
                api = Some(match tts {
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "gl"
                        => (registry::Gl, khronos_api::GL_XML),
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "glx"
                        => (registry::Glx, khronos_api::GLX_XML),
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "wgl"
                        => (registry::Wgl, khronos_api::WGL_XML),
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "egl"
                        => (registry::Egl, khronos_api::EGL_XML),
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "gles1"
                        => (registry::Gles1, khronos_api::GL_XML),
                    [&TtToken(_, token::LIT_STR(api))] if api.as_str() == "gles2"
                        => (registry::Gles2, khronos_api::GL_XML),
                    [&TtToken(span, token::LIT_STR(api))] => {
                        ecx.span_err(span, format!("Unknown API \"{}\"", api.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| tt.get_span());
                        ecx.span_err(span, "Invalid API format, expected \
                                            string.");
                        return DummyResult::any(span);
                    }
                })
            }
            ("profile", tts) => {
                if profile.is_some() {
                    ecx.span_err(tts[0].get_span(), "A profile was already specified.");
                    return DummyResult::any(span);
                }
                profile = Some(match tts {
                    [&TtToken(_, token::LIT_STR(profile))] if profile.as_str() == "core"
                        => "core".to_string(),
                    [&TtToken(_, token::LIT_STR(profile))] if profile.as_str() == "compatibility"
                        => "compatibility".to_string(),
                    [&TtToken(_, token::LIT_STR(profile))] => {
                        let span = tts.head().map_or(span, |tt| tt.get_span());
                        ecx.span_err(span, format!("Unknown profile \"{}\"",
                                                   profile.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| tt.get_span());
                        ecx.span_err(span, "Invalid profile format, expected \
                                            string.");
                        return DummyResult::any(span);
                    },
                })
            }
            ("version", tts) => {
                if version.is_some() {
                    let span = tts.head().map_or(span, |tt| tt.get_span());
                    ecx.span_err(span, "A version was already specified.");
                    return DummyResult::any(span);
                }
                version = Some(match tts {
                    [&TtToken(_, token::LIT_STR(version))] => {
                        version.as_str().to_string()
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| tt.get_span());
                        ecx.span_err(span, "Invalid version format, expected \
                                            string.");
                        return DummyResult::any(span);
                    },
                });
            }
            ("generator", tts) => {
                if generator.is_some() {
                    let span = tts.head().map_or(span, |tt| tt.get_span());
                    ecx.span_err(span, "A generator was already specified.");
                    return DummyResult::any(span);
                }
                generator = Some(match tts {
                    [&TtToken(_, token::LIT_STR(gen))] if gen.as_str() == "global"
                        => box generators::global_gen::GlobalGenerator as Box<Generator>,
                    [&TtToken(_, token::LIT_STR(gen))] if gen.as_str() == "struct"
                        => box generators::struct_gen::StructGenerator as Box<Generator>,
                    [&TtToken(_, token::LIT_STR(gen))] if gen.as_str() == "static"
                        => box generators::static_gen::StaticGenerator as Box<Generator>,
                    [&TtToken(span, token::LIT_STR(gen))] => {
                        ecx.span_err(span, format!("Unknown generator \"{}\"",
                                                   gen.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| tt.get_span());
                        ecx.span_err(span, "Invalid generator format, expected \
                                            string.");
                        return DummyResult::any(span);
                    },
                });
            }
            ("extensions", tts) => {
                if extensions.is_some() {
                    let span = tts.head().map_or(span, |tt| tt.get_span());
                    ecx.span_err(span, "The list of extensions were already \
                                        specified.");
                    return DummyResult::any(span);
                }
                extensions = Some(match tts {
                    [&TtDelimited(span, ref delimited)] => {
                        let (ref begin, ref tts, ref close) = **delimited;
                        match (&begin.token, &close.token) {
                            (&token::LBRACKET, &token::RBRACKET) => {
                                // Drop the trailing comma if it exists
                                let tts = drop_trailing_comma(tts.as_slice());

                                // Collect the extensions, breaking early if a parse
                                // error occurs.
                                let mut failed = false;
                                let exts = tts.split(is_comma).scan((), |_, tts| {
                                    match tts {
                                        [TtToken(_, token::LIT_STR(ext))] => {
                                            Some(ext.as_str().to_string())
                                        },
                                        _ => {
                                            failed = true;
                                            None
                                        },
                                    }
                                }).collect();

                                // Cause an error if there is still some leftover
                                // tokens.
                                if failed {
                                    ecx.span_err(span, "Invalid extension format, \
                                                        expected string.");
                                    return DummyResult::any(span);
                                } else {
                                    exts
                                }
                            },
                            (_, _) => {
                                ecx.span_err(span, "Expected a comma separated \
                                                    list of extension strings \
                                                    delimited by square brackets: \
                                                    `[]`");
                                return DummyResult::any(span);
                            },
                        }
                    },
                    _ => {
                        ecx.span_err(span, "Expected a comma separated list of \
                                            extension strings.");
                        return DummyResult::any(span);
                    },
                });
            }
            (field, _) => {
                ecx.span_err(span, format!("Unknown field `{}`", field).as_slice());
                return DummyResult::any(span);
            }
        }
    }

    // Use default values if the parameters have not been set
    let (ns, source) = api.unwrap_or((registry::Gl, khronos_api::GL_XML));
    let extensions = extensions.unwrap_or(vec![]);
    let version = version.unwrap_or("1.0".to_string());
    let generator = generator.unwrap_or(box generators::static_gen::StaticGenerator);
    let profile = profile.unwrap_or("core".to_string());

    // Get generator field values, using default values if they have not been
    // specified
    let filter = Some(Filter {
        api: ns.to_string(),
        extensions: extensions,
        version: version,
        profile: profile,
    });

    // Generate the registry of all bindings
    let registry = {
        use std::io::BufReader;
        use std::task;

        let result = task::try(proc() {
            let reader = BufReader::new(source);
            Registry::from_xml(reader, ns, filter)
        });

        match result {
            Ok(reg) => reg,
            Err(err) => {
                use std::any::{Any, AnyRefExt};
                let err: &Any = &*err;

                match err {
                    err if err.is::<String>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<String>().unwrap().as_slice());
                    },
                    err if err.is::<&'static str>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<&'static str>().unwrap().as_slice());
                    },
                    _ => {
                        ecx.span_err(span, "unknown error while parsing the registry");
                    }
                }

                return DummyResult::any(span);
            }
        }
    };

    // generating the Rust bindings as a source code into "buffer"
    let items = generator.write(ecx, &registry, ns);

    MacItems::new(items.into_iter())
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2014 Jeremy Letang
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//! A syntax extension to handle route attributes

#![crate_id = "github.com/JeremyLetang/web_dispatcher#route_macros:0.0.1"]
#![desc = "route macros for Rust"]
#![license = "mit"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![experimental]
#![allow(missing_doc)]
#![feature(macro_registrar, managed_boxes, quote)]

extern crate syntax;

use std::local_data;

use syntax::ast;
use syntax::codemap::Span;
use syntax::parse::token;
use syntax::ast::{Ident, TokenTree, Expr, Name, ExprVec, MetaItem, MetaNameValue, LitStr, Item,
                  ItemFn};
use syntax::ext::base::{ExtCtxt, MacResult, SyntaxExtension, BasicMacroExpander, NormalTT,
                        ItemModifier, MacExpr};

use syntax::ast::Path;
use syntax::ast::PathSegment;
use syntax::owned_slice::OwnedSlice;
use syntax::ast::ExprPath;

// Store routes in a local data (vector of path ident, associated route, method as a string)
static routes: local_data::Key<Vec<(Vec<Ident>, ~str, ~str)>> = &local_data::Key;

fn local_data_get_or_init() -> Vec<(Vec<Ident>, ~str,~str)> {
    local_data::get(routes, |d| {
        match d {
            Some(v) =>  v.clone(),
            None => Vec::new()
        }
    })
}

#[doc(hidden)]
#[macro_registrar]
pub fn registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("route"), ItemModifier(expand_route));
    register(token::intern("method"), ItemModifier(expand_method));
    register(token::intern("routes"),
             NormalTT(~BasicMacroExpander {
                expander: expand_get_routes,
                span: None,
             },
             None));
}

fn expand_get_routes(cx: &mut ExtCtxt, sp: Span, _: &[TokenTree]) -> ~MacResult {
    let v = local_data_get_or_init();
    let v = v.iter().map(|&(ref f, ref s, ref m)| {
        let p = create_func_path_expr(f, sp);
        quote_expr!(&*cx, ($p, $s, $m))
    }).collect();
    let v = create_slice_expr(v, sp);
    MacExpr::new(quote_expr!(cx, $v.to_owned()))
}

// create the path expression
fn create_func_path_expr(vec_ident: &Vec<Ident>, sp: Span) -> @Expr {
    // create the list of path segment from the idents
    let segs = vec_ident.iter().fold(Vec::new(), |mut v, &i| {
        v.push(PathSegment {
            identifier: i,
            lifetimes: Vec::new(),
            types: OwnedSlice::empty(),
        });
        v
    });
    // create the complete Path from the segments
    let func_path = Path {
        span: sp,
        global: false,
        segments: segs,
    };
    @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprPath(func_path),
        span: sp
    }
}

// create a slice from the vector of (path / routes)
fn create_slice_expr(vec: Vec<@Expr>, sp: Span) -> @Expr {
    @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(vec),
        span: sp
    }
}

fn expand_route(cx: &mut ExtCtxt, sp: Span, meta_item: @MetaItem, item: @Item) -> @Item {
    match item.node {
        ItemFn(_, _, _, _, _) => {
            get_route_attr_value(cx, sp, meta_item, item);
            item
        },
        _ => {
            cx.span_err(sp, "route attribute can only be used on functions");
            item
        }
    }
}

fn get_route_attr_value(cx: &mut ExtCtxt, sp: Span, meta_item: @MetaItem, item: @Item) {
    match meta_item.node {
        MetaNameValue(_, ref l) => {
            match l.node {
                LitStr(ref s, _) => {
                    // check if the route already exist.
                    let route_attr = s.get().to_owned();
                    if !route_already_exist(&route_attr) {
                        insert_route(cx, item, route_attr);
                    } else {
                        cx.span_err(sp, "this route already exist for an other function")
                    }
                },
                _ => cx.span_err(sp, "route attribute can only use literal str")
            }
        }
        _ => cx.span_err(sp, "route attribute must be on the form: \
            #[route = \"my/route/\"] pub fn my_route()")
    }
}

// insert the route in the local data
fn insert_route(cx: &mut ExtCtxt, item: @Item, route_attr: ~str) {
    let v = local_data_get_or_init();
    // retrieve the complete path of the function
    let mut vec_ident = cx.mod_path.clone();
    // concatenate the name of the function
    vec_ident.push(item.ident);
    // insert the route and save
    let mut method = "GET".to_owned();
    let mut v: Vec<(Vec<Ident>, ~str, ~str)> = v.move_iter().filter(|&(ref v_i, _, ref m)| {
        if v_i == &vec_ident {
            method = m.clone();
            false
        } else { true }
    }).collect();
    v.push((vec_ident, route_attr, method));
    local_data::set(routes, v);
}

// check if a route is already defined for an other function
fn route_already_exist(route: &~str) -> bool {
    let v = local_data_get_or_init();
    v.iter().fold(false, |b, &(_, ref s, _)| { b || s == route })
}

fn expand_method(cx: &mut ExtCtxt, sp: Span, meta_item: @MetaItem, item: @Item) -> @Item {
    match item.node {
        ItemFn(_, _, _, _, _) => {
            get_method_attr_value(cx, sp, meta_item, item);
            item
        },
        _ => {
            cx.span_err(sp, "method attribute can only be used on functions");
            item
        }
    }
}

fn get_method_attr_value(cx: &mut ExtCtxt, sp: Span, meta_item: @MetaItem, item: @Item) {
    match meta_item.node {
        MetaNameValue(_, ref l) => {
            match l.node {
                LitStr(ref s, _) => {
                    let method_attr = s.get();
                    if is_method_attribute_valid(method_attr) {
                        insert_method(cx, item, method_attr.to_owned());
                    } else {
                        cx.span_err(sp, "this method attribut don't exist. Here is a list of \
                            available attribute: [GET, POST]")
                    }
                },
                _ => cx.span_err(sp, "method attribute can only use literal str")
            }
        }
        _ => cx.span_err(sp, "method attribute must be on the form: \
            #[method = \"GET\"] pub fn my_route()")
    }
}

fn is_method_attribute_valid(attr: &str) -> bool {
    match attr {
        "GET" | "POST" => true,
        _ => false
    }
}

// insert the method in the local data
fn insert_method(cx: &mut ExtCtxt, item: @Item, method_attr: ~str) {
    let v = local_data_get_or_init();
    // retrieve the complete path of the function
    let mut vec_ident = cx.mod_path.clone();
    // concatenate the name of the function
    vec_ident.push(item.ident);
    // insert the route and save
    let mut route = "".to_owned();
    let mut v: Vec<(Vec<Ident>, ~str, ~str)> = v.move_iter().filter(|&(ref v_i, ref r, _)| {
        if v_i == &vec_ident {
            route = r.clone();
            false
        } else { true }
    }).collect();
    v.push((vec_ident, route, method_attr));
    local_data::set(routes, v);
}
<commit_msg>clean libroute_macro code<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2014 Jeremy Letang
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//! A syntax extension to handle route attributes

#![crate_id = "github.com/JeremyLetang/web_dispatcher#route_macros:0.0.1"]
#![desc = "route macros for Rust"]
#![license = "mit"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![experimental]
#![allow(missing_doc)]
#![feature(macro_registrar, managed_boxes, quote)]

extern crate syntax;

use std::local_data;

use syntax::ast;
use syntax::ast::Path;
use syntax::parse::token;
use syntax::ast::ExprPath;
use syntax::codemap::Span;
use syntax::ast::PathSegment;
use syntax::owned_slice::OwnedSlice;
use syntax::ast::{Ident,
                  TokenTree,
                  Expr,
                  Name,
                  ExprVec,
                  MetaItem,
                  MetaNameValue,
                  LitStr,
                  Item,
                  ItemFn};
use syntax::ext::base::{ExtCtxt,
                        MacResult,
                        SyntaxExtension,
                        BasicMacroExpander,
                        NormalTT,
                        ItemModifier,
                        MacExpr};

// Store routes in a local data (vector of path ident, associated route, method as a string)
static routes: local_data::Key<Vec<(Vec<Ident>, ~str, ~str)>> = &local_data::Key;

fn local_data_get_or_init() -> Vec<(Vec<Ident>, ~str,~str)> {
    local_data::get(routes, |d| {
        match d {
            Some(v) =>  v.clone(),
            None => Vec::new()
        }
    })
}

#[doc(hidden)]
#[macro_registrar]
pub fn registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("route"), ItemModifier(expand_route));
    register(token::intern("method"), ItemModifier(expand_method));
    register(token::intern("routes"),
             NormalTT(~BasicMacroExpander {
                expander: expand_get_routes,
                span: None,
             },
             None));
}

fn expand_get_routes(cx: &mut ExtCtxt, sp: Span, _: &[TokenTree]) -> ~MacResult {
    let v = local_data_get_or_init();
    let v = v.iter().map(|&(ref f, ref s, ref m)| {
        let p = create_func_path_expr(f, sp);
        quote_expr!(&*cx, ($p, $s, $m))
    }).collect();
    let v = create_slice_expr(v, sp);
    MacExpr::new(quote_expr!(cx, $v.to_owned()))
}

// create the path expression
fn create_func_path_expr(vec_ident: &Vec<Ident>, sp: Span) -> @Expr {
    // create the list of path segment from the idents
    let segs = vec_ident.iter().fold(Vec::new(), |mut v, &i| {
        v.push(PathSegment {
            identifier: i,
            lifetimes: Vec::new(),
            types: OwnedSlice::empty(),
        });
        v
    });
    // create the complete Path from the segments
    let func_path = Path {
        span: sp,
        global: false,
        segments: segs,
    };
    @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprPath(func_path),
        span: sp
    }
}

// create a slice from the vector of (path / routes)
fn create_slice_expr(vec: Vec<@Expr>, sp: Span) -> @Expr {
    @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(vec),
        span: sp
    }
}

fn expand_route(cx: &mut ExtCtxt,
                sp: Span,
                meta_item: @MetaItem,
                item: @Item)
                -> @Item {
    match item.node {
        ItemFn(_, _, _, _, _) => {
            get_route_attr_value(cx, sp, meta_item, item);
            item
        },
        _ => {
            cx.span_err(sp, "route attribute can only be used on functions");
            item
        }
    }
}

fn get_route_attr_value(cx: &mut ExtCtxt,
                        sp: Span,
                        meta_item: @MetaItem,
                        item: @Item) {
    match meta_item.node {
        MetaNameValue(_, ref l) => {
            match l.node {
                LitStr(ref s, _) => {
                    // check if the route already exist.
                    let route_attr = s.get().to_owned();
                    if !route_already_exist(&route_attr) {
                        insert_route(cx, item, route_attr);
                    } else {
                        cx.span_err(sp, "this route already exist for an other function")
                    }
                },
                _ => cx.span_err(sp, "route attribute can only use literal str")
            }
        }
        _ => cx.span_err(sp, "route attribute must be on the form: \
            #[route = \"my/route/\"] pub fn my_route()")
    }
}

// insert the route in the local data
fn insert_route(cx: &mut ExtCtxt,
                item: @Item,
                route_attr: ~str) {
    let v = local_data_get_or_init();
    // retrieve the complete path of the function
    let mut vec_ident = cx.mod_path.clone();
    // concatenate the name of the function
    vec_ident.push(item.ident);
    // insert the route and save
    let mut method = "GET".to_owned();
    let mut v: Vec<(Vec<Ident>, ~str, ~str)> = v.move_iter().filter(|&(ref v_i, _, ref m)| {
        if v_i == &vec_ident {
            method = m.clone();
            false
        } else { true }
    }).collect();
    v.push((vec_ident, route_attr, method));
    local_data::set(routes, v);
}

// check if a route is already defined for an other function
fn route_already_exist(route: &~str) -> bool {
    let v = local_data_get_or_init();
    v.iter().fold(false, |b, &(_, ref s, _)| { b || s == route })
}

fn expand_method(cx: &mut ExtCtxt, sp: Span, meta_item: @MetaItem, item: @Item) -> @Item {
    match item.node {
        ItemFn(_, _, _, _, _) => {
            get_method_attr_value(cx, sp, meta_item, item);
            item
        },
        _ => {
            cx.span_err(sp, "method attribute can only be used on functions");
            item
        }
    }
}

fn get_method_attr_value(cx: &mut ExtCtxt,
                         sp: Span,
                         meta_item: @MetaItem,
                         item: @Item) {
    match meta_item.node {
        MetaNameValue(_, ref l) => {
            match l.node {
                LitStr(ref s, _) => {
                    let method_attr = s.get();
                    if is_method_attribute_valid(method_attr) {
                        insert_method(cx, item, method_attr.to_owned());
                    } else {
                        cx.span_err(sp, "this method attribut don't exist. Here is a list of \
                            available attribute: [GET, POST]")
                    }
                },
                _ => cx.span_err(sp, "method attribute can only use literal str")
            }
        }
        _ => cx.span_err(sp, "method attribute must be on the form: \
            #[method = \"GET\"] pub fn my_route()")
    }
}

fn is_method_attribute_valid(attr: &str) -> bool {
    match attr {
        "GET" | "POST" => true,
        _ => false
    }
}

// insert the method in the local data
fn insert_method(cx: &mut ExtCtxt,
                 item: @Item,
                 method_attr: ~str) {
    let v = local_data_get_or_init();
    // retrieve the complete path of the function
    let mut vec_ident = cx.mod_path.clone();
    // concatenate the name of the function
    vec_ident.push(item.ident);
    // insert the route and save
    let mut route = "".to_owned();
    let mut v: Vec<(Vec<Ident>, ~str, ~str)> = v.move_iter().filter(|&(ref v_i, ref r, _)| {
        if v_i == &vec_ident {
            route = r.clone();
            false
        } else { true }
    }).collect();
    v.push((vec_ident, route, method_attr));
    local_data::set(routes, v);
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
 * This module implements the Hmac function - a Message Authentication Code using a Digest.
 */

use std::slice;

use digest::Digest;
use mac::{Mac, MacResult};

/**
 * The Hmac struct represents an Hmac function - a Message Authentication Code using a Digest.
 */
pub struct Hmac<D> {
    digest: D,
    i_key: Vec<u8>,
    o_key: Vec<u8>,
    finished: bool
}

fn derive_key(key: &mut [u8], mask: u8) {
    for elem in key.mut_iter() {
        *elem ^= mask;
    }
}

// The key that Hmac processes must be the same as the block size of the underlying Digest. If the
// provided key is smaller than that, we just pad it with zeros. If its larger, we hash it and then
// pad it with zeros.
fn expand_key<D: Digest>(digest: &mut D, key: &[u8]) -> Vec<u8> {
    let bs = digest.block_size();
    let mut expanded_key = Vec::from_elem(bs, 0u8);
    if key.len() <= bs {
        slice::bytes::copy_memory(expanded_key.as_mut_slice(), key);
        for elem in expanded_key.mut_slice_from(key.len()).mut_iter() {
            *elem = 0;
        }
    } else {
        let output_size = digest.output_bytes();
        digest.input(key);
        digest.result(expanded_key.mut_slice_to(output_size));
        digest.reset();
        for elem in expanded_key.mut_slice_from(output_size).mut_iter() {
            *elem = 0;
        }
    }
    return expanded_key;
}

// Hmac uses two keys derived from the provided key - one by xoring every byte with 0x36 and another
// with 0x5c.
fn create_keys<D: Digest>(digest: &mut D, key: &[u8]) -> (Vec<u8>, Vec<u8>) {
    let mut i_key = expand_key(digest, key);
    let mut o_key = i_key.clone();
    derive_key(i_key.as_mut_slice(), 0x36);
    derive_key(o_key.as_mut_slice(), 0x5c);
    return (i_key, o_key);
}

impl <D: Digest> Hmac<D> {
    /**
     * Create a new Hmac instance.
     *
     * # Arguments
     * * digest - The Digest to use.
     * * key - The key to use.
     *
     */
    pub fn new(mut digest: D, key: &[u8]) -> Hmac<D> {
        let (i_key, o_key) = create_keys(&mut digest, key);
        digest.input(i_key.as_slice());
        return Hmac {
            digest: digest,
            i_key: i_key,
            o_key: o_key,
            finished: false
        }
    }
}

impl <D: Digest> Mac for Hmac<D> {
    fn input(&mut self, data: &[u8]) {
        assert!(!self.finished);
        self.digest.input(data);
    }

    fn reset(&mut self) {
        self.digest.reset();
        self.digest.input(self.i_key.as_slice());
        self.finished = false;
    }

    fn result(&mut self) -> MacResult {
        let output_size = self.digest.output_bytes();
        let mut code = Vec::from_elem(output_size, 0u8);

        self.raw_result(code.as_mut_slice());

        return MacResult::new_from_owned(code);
    }

    fn raw_result(&mut self, output: &mut [u8]) {
        if !self.finished {
            self.digest.result(output);

            self.digest.reset();
            self.digest.input(self.o_key.as_slice());
            self.digest.input(output);

            self.finished = true;
        }

        self.digest.result(output);
    }

    fn output_bytes(&self) -> uint { self.digest.output_bytes() }
}

#[cfg(test)]
mod test {
    use mac::{Mac, MacResult};
    use hmac::Hmac;
    use digest::Digest;
    use md5::Md5;

    struct Test {
        key: ~[u8],
        data: ~[u8],
        expected: ~[u8]
    }

    // Test vectors from: http://tools.ietf.org/html/rfc2104

    fn tests() -> ~[Test] {
        return ~[
            Test {
                key: ~[0x0b, ..16],
                data: "Hi There".as_bytes().to_owned(),
                expected: ~[
                    0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c,
                    0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d ]
            },
            Test {
                key: "Jefe".as_bytes().to_owned(),
                data: "what do ya want for nothing?".as_bytes().to_owned(),
                expected: ~[
                    0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03,
                    0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38 ]
            },
            Test {
                key: ~[0xaa, ..16],
                data: ~[0xdd, ..50],
                expected: ~[
                    0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88,
                    0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6 ]
            }
        ];
    }

    #[test]
    fn test_hmac_md5() {
        let tests = tests();
        for t in tests.iter() {
            let mut hmac = Hmac::new(Md5::new(), t.key);

            hmac.input(t.data);
            let result = hmac.result();
            let expected = MacResult::new(t.expected);
            assert!(result == expected);

            hmac.reset();

            hmac.input(t.data);
            let result2 = hmac.result();
            let expected2 = MacResult::new(t.expected);
            assert!(result2 == expected2);
        }
    }

    #[test]
    fn test_hmac_md5_incremental() {
        let tests = tests();
        for t in tests.iter() {
            let mut hmac = Hmac::new(Md5::new(), t.key);
            for i in range(0, t.data.len()) {
                hmac.input(t.data.slice(i, i + 1));
            }
            let result = hmac.result();
            let expected = MacResult::new(t.expected);
            assert!(result == expected);
        }
    }
}
<commit_msg>hmac: Don't zero memory guaranteed to be zero<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
 * This module implements the Hmac function - a Message Authentication Code using a Digest.
 */

use std::slice;

use digest::Digest;
use mac::{Mac, MacResult};

/**
 * The Hmac struct represents an Hmac function - a Message Authentication Code using a Digest.
 */
pub struct Hmac<D> {
    digest: D,
    i_key: Vec<u8>,
    o_key: Vec<u8>,
    finished: bool
}

fn derive_key(key: &mut [u8], mask: u8) {
    for elem in key.mut_iter() {
        *elem ^= mask;
    }
}

// The key that Hmac processes must be the same as the block size of the underlying Digest. If the
// provided key is smaller than that, we just pad it with zeros. If its larger, we hash it and then
// pad it with zeros.
fn expand_key<D: Digest>(digest: &mut D, key: &[u8]) -> Vec<u8> {
    let bs = digest.block_size();
    let mut expanded_key = Vec::from_elem(bs, 0u8);
    if key.len() <= bs {
        slice::bytes::copy_memory(expanded_key.as_mut_slice(), key);
    } else {
        let output_size = digest.output_bytes();
        digest.input(key);
        digest.result(expanded_key.mut_slice_to(output_size));
        digest.reset();
    }
    return expanded_key;
}

// Hmac uses two keys derived from the provided key - one by xoring every byte with 0x36 and another
// with 0x5c.
fn create_keys<D: Digest>(digest: &mut D, key: &[u8]) -> (Vec<u8>, Vec<u8>) {
    let mut i_key = expand_key(digest, key);
    let mut o_key = i_key.clone();
    derive_key(i_key.as_mut_slice(), 0x36);
    derive_key(o_key.as_mut_slice(), 0x5c);
    return (i_key, o_key);
}

impl <D: Digest> Hmac<D> {
    /**
     * Create a new Hmac instance.
     *
     * # Arguments
     * * digest - The Digest to use.
     * * key - The key to use.
     *
     */
    pub fn new(mut digest: D, key: &[u8]) -> Hmac<D> {
        let (i_key, o_key) = create_keys(&mut digest, key);
        digest.input(i_key.as_slice());
        return Hmac {
            digest: digest,
            i_key: i_key,
            o_key: o_key,
            finished: false
        }
    }
}

impl <D: Digest> Mac for Hmac<D> {
    fn input(&mut self, data: &[u8]) {
        assert!(!self.finished);
        self.digest.input(data);
    }

    fn reset(&mut self) {
        self.digest.reset();
        self.digest.input(self.i_key.as_slice());
        self.finished = false;
    }

    fn result(&mut self) -> MacResult {
        let output_size = self.digest.output_bytes();
        let mut code = Vec::from_elem(output_size, 0u8);

        self.raw_result(code.as_mut_slice());

        return MacResult::new_from_owned(code);
    }

    fn raw_result(&mut self, output: &mut [u8]) {
        if !self.finished {
            self.digest.result(output);

            self.digest.reset();
            self.digest.input(self.o_key.as_slice());
            self.digest.input(output);

            self.finished = true;
        }

        self.digest.result(output);
    }

    fn output_bytes(&self) -> uint { self.digest.output_bytes() }
}

#[cfg(test)]
mod test {
    use mac::{Mac, MacResult};
    use hmac::Hmac;
    use digest::Digest;
    use md5::Md5;

    struct Test {
        key: ~[u8],
        data: ~[u8],
        expected: ~[u8]
    }

    // Test vectors from: http://tools.ietf.org/html/rfc2104

    fn tests() -> ~[Test] {
        return ~[
            Test {
                key: ~[0x0b, ..16],
                data: "Hi There".as_bytes().to_owned(),
                expected: ~[
                    0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c,
                    0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d ]
            },
            Test {
                key: "Jefe".as_bytes().to_owned(),
                data: "what do ya want for nothing?".as_bytes().to_owned(),
                expected: ~[
                    0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03,
                    0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38 ]
            },
            Test {
                key: ~[0xaa, ..16],
                data: ~[0xdd, ..50],
                expected: ~[
                    0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88,
                    0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6 ]
            }
        ];
    }

    #[test]
    fn test_hmac_md5() {
        let tests = tests();
        for t in tests.iter() {
            let mut hmac = Hmac::new(Md5::new(), t.key);

            hmac.input(t.data);
            let result = hmac.result();
            let expected = MacResult::new(t.expected);
            assert!(result == expected);

            hmac.reset();

            hmac.input(t.data);
            let result2 = hmac.result();
            let expected2 = MacResult::new(t.expected);
            assert!(result2 == expected2);
        }
    }

    #[test]
    fn test_hmac_md5_incremental() {
        let tests = tests();
        for t in tests.iter() {
            let mut hmac = Hmac::new(Md5::new(), t.key);
            for i in range(0, t.data.len()) {
                hmac.input(t.data.slice(i, i + 1));
            }
            let result = hmac.result();
            let expected = MacResult::new(t.expected);
            assert!(result == expected);
        }
    }
}
<|endoftext|>


<commit_before>use core::iter::Iterator;
use core::iter::range_step;
use core::option::*;
use core::ptr::offset;

use con::newline;
use con::write;
use con::writePHex;
use con::writeUInt;
use mboot;
use mboot::MemoryMapItem;
use start32::PhysAddr;
use start32::MutPhysAddr;
use util::abort;

use mem::framestack::*;

extern {
fn memset(dst : *mut u8, v : u8, count : uint);
}

pub mod framestack {

	use core::option::*;

	struct FreeFrame {
		next : FreeFrameS
	}
	pub type FreeFrameS = *mut FreeFrame;
	pub type FreeFrameP = Option<*mut FreeFrame>;

	pub static none : FreeFrameS = 0 as *mut FreeFrame;

	fn from_option<T>(x : Option<T>, def : T) -> T {
		match x {
			Some(val) => val,
			None => def
		}
	}

	pub fn store(p : FreeFrameP) -> FreeFrameS {
		from_option(p, none)
	}

	pub fn push_frame<T>(head : &mut FreeFrameS, frame : *mut T) {
		let free = frame as *mut FreeFrame;
		unsafe { (*free).next = *head; }
		*head = free;
	}

	pub fn pop_frame(head : &mut FreeFrameS) -> FreeFrameP {
		if *head == none {
			None
		} else {
			let page = *head;
			unsafe {
				*head = (*page).next;
				(*page).next = none;
			}
			Some(page)
		}
	}

}

pub struct Global {
	// Frames that are uninitialized (except for the first word)
	garbage : FreeFrameS,
	// Frames that are all zeroes except for the first word
	free : FreeFrameS,
	num_used : uint,
	num_total : uint,
}

pub static empty_global : Global = Global { garbage : none, free : none, num_used : 0, num_total : 0 };
pub static mut global : Global = empty_global;

pub struct PerCpu {
	free : FreeFrameS
}

struct MemoryMap {
	addr : *u8,
	end : *u8
}

impl MemoryMap {
	fn new(addr : *u8, length : uint) -> MemoryMap {
		return MemoryMap { addr : addr, end : unsafe { offset(addr, length as int) } }
	}
}

impl Iterator<MemoryMapItem> for MemoryMap {
	fn next(&mut self) -> Option<MemoryMapItem> {
		if self.addr < self.end { unsafe {
			let item = *(self.addr as *MemoryMapItem);
			self.addr = offset(self.addr, 4 + item.item_size as int);
			Some(item)
		} } else {
			None
		}
	}
}

fn clear<T>(page : *mut T) {
	unsafe { memset(page as *mut u8, 0, 4096); }
}

impl Global {
	pub fn init(&mut self, info : &mboot::Info, min_addr : uint) {
		if !info.has(mboot::MemoryMap) {
			return;
		}

		let mut mmap = MemoryMap::new(PhysAddr(info.mmap_addr as uint), info.mmap_length as uint);
		let mut count = 0;
		for item in mmap {
			newline();
			writePHex(item.start as uint);
			newline();
			writePHex(item.length as uint);
			newline();
			writeUInt(item.item_type as uint);
			newline();
			for p in range_step(item.start, item.start + item.length, 4096) {
				if p as uint > min_addr {
					self.free_frame(MutPhysAddr(p as uint));
					count += 1;
				}
			}
		}
		self.num_used = 0;
		self.num_total = count;
	}

	pub fn free_frame(&mut self, vpaddr : *mut u8) {
		self.num_used -= 1;
		push_frame(&mut self.garbage, vpaddr);
	}

	pub fn alloc_frame(&mut self) -> FreeFrameP {
		match pop_frame(&mut self.free) {
			Some(page) => {
				self.num_used += 1;
				Some(page)
			},
			None => match pop_frame(&mut self.garbage) {
				Some(page) => {
					self.num_used += 1;
					clear(page);
					Some(page)
				},
				None => { None }
			}
		}
	}

	pub fn free_pages(&self) -> uint {
		self.num_total - self.num_used
	}

	pub fn used_pages(&self) -> uint {
		self.num_used
	}

	#[inline(never)]
	pub fn stat(&self) {
		write("Free: ");
		writeUInt(self.free_pages() * 4);
		write("KiB, Used: ");
		writeUInt(self.used_pages() * 4);
		write("KiB\n");
	}
}

#[inline(always)]
pub fn get() -> &mut Global {
	unsafe { &mut global }
}

impl PerCpu {
	pub fn new() -> PerCpu {
		PerCpu { free : none }
	}

	pub fn alloc_frame(&mut self) -> Option<*mut u8> {
		match pop_frame(&mut self.free) {
			Some(page) => { return Some(page as *mut u8); }
			None => {}
		}
		self.free = store(get().alloc_frame());
		return self.steal_frame();
	}

	pub fn steal_frame(&mut self) -> Option<*mut u8> {
		match get().alloc_frame() {
			Some(page) => Some(page as *mut u8),
			None => None
		}
	}

	pub fn alloc_frame_panic(&mut self) -> *mut u8 {
		match self.alloc_frame() {
			Some(page) => page,
			None => abort()
		}
	}

	pub fn free_frame(&mut self, page : *mut u8) {
		get().free_frame(page);
	}

	pub fn test(&mut self) {
		let mut head = none;
		let mut count = 0;
		loop {
			let p = self.alloc_frame();
//			write("Allocation #");
//			writeUInt(count);
//			write(": ");
//			writePtr(from_option(p, 0 as *mut u8) as *u8);
//			newline();
//			get().stat();
			match p {
				Some(pp) => push_frame(&mut head, pp),
				None => break
			}
			count += 1;
		}
		write("Allocated everything: ");
		writeUInt(count);
		write(" pages\n");
		get().stat();
		loop {
//			write("Allocation #");
//			writeUInt(count);
//			write(": ");
//			writePtr(from_option(head, 0 as *mut FreeFrame) as *u8);
//			newline();
//			get().stat();
			match pop_frame(&mut head) {
				Some(p) => self.free_frame(p as *mut u8),
				None => break
			}
			count -= 1;
		}
	}
}
<commit_msg>More uninlining<commit_after>use core::iter::Iterator;
use core::iter::range_step;
use core::option::*;
use core::ptr::offset;

use con::newline;
use con::write;
use con::writePHex;
use con::writeUInt;
use mboot;
use mboot::MemoryMapItem;
use start32::PhysAddr;
use start32::MutPhysAddr;
use util::abort;

use mem::framestack::*;

extern {
fn memset(dst : *mut u8, v : u8, count : uint);
}

pub mod framestack {

	use core::option::*;

	struct FreeFrame {
		next : FreeFrameS
	}
	pub type FreeFrameS = *mut FreeFrame;
	pub type FreeFrameP = Option<*mut FreeFrame>;

	pub static none : FreeFrameS = 0 as *mut FreeFrame;

	fn from_option<T>(x : Option<T>, def : T) -> T {
		match x {
			Some(val) => val,
			None => def
		}
	}

	pub fn store(p : FreeFrameP) -> FreeFrameS {
		from_option(p, none)
	}

	pub fn push_frame<T>(head : &mut FreeFrameS, frame : *mut T) {
		let free = frame as *mut FreeFrame;
		unsafe { (*free).next = *head; }
		*head = free;
	}

	pub fn pop_frame(head : &mut FreeFrameS) -> FreeFrameP {
		if *head == none {
			None
		} else {
			let page = *head;
			unsafe {
				*head = (*page).next;
				(*page).next = none;
			}
			Some(page)
		}
	}

}

pub struct Global {
	// Frames that are uninitialized (except for the first word)
	garbage : FreeFrameS,
	// Frames that are all zeroes except for the first word
	free : FreeFrameS,
	num_used : uint,
	num_total : uint,
}

pub static empty_global : Global = Global { garbage : none, free : none, num_used : 0, num_total : 0 };
pub static mut global : Global = empty_global;

pub struct PerCpu {
	free : FreeFrameS
}

struct MemoryMap {
	addr : *u8,
	end : *u8
}

impl MemoryMap {
	fn new(addr : *u8, length : uint) -> MemoryMap {
		return MemoryMap { addr : addr, end : unsafe { offset(addr, length as int) } }
	}
}

impl Iterator<MemoryMapItem> for MemoryMap {
	fn next(&mut self) -> Option<MemoryMapItem> {
		if self.addr < self.end { unsafe {
			let item = *(self.addr as *MemoryMapItem);
			self.addr = offset(self.addr, 4 + item.item_size as int);
			Some(item)
		} } else {
			None
		}
	}
}

fn clear<T>(page : *mut T) {
	unsafe { memset(page as *mut u8, 0, 4096); }
}

impl Global {
	pub fn init(&mut self, info : &mboot::Info, min_addr : uint) {
		if !info.has(mboot::MemoryMap) {
			return;
		}

		let mut mmap = MemoryMap::new(PhysAddr(info.mmap_addr as uint), info.mmap_length as uint);
		let mut count = 0;
		for item in mmap {
			newline();
			writePHex(item.start as uint);
			newline();
			writePHex(item.length as uint);
			newline();
			writeUInt(item.item_type as uint);
			newline();
			for p in range_step(item.start, item.start + item.length, 4096) {
				if p as uint > min_addr {
					self.free_frame(MutPhysAddr(p as uint));
					count += 1;
				}
			}
		}
		self.num_used = 0;
		self.num_total = count;
	}

	pub fn free_frame(&mut self, vpaddr : *mut u8) {
		self.num_used -= 1;
		push_frame(&mut self.garbage, vpaddr);
	}

	pub fn alloc_frame(&mut self) -> FreeFrameP {
		match pop_frame(&mut self.free) {
			Some(page) => {
				self.num_used += 1;
				Some(page)
			},
			None => match pop_frame(&mut self.garbage) {
				Some(page) => {
					self.num_used += 1;
					clear(page);
					Some(page)
				},
				None => { None }
			}
		}
	}

	pub fn free_pages(&self) -> uint {
		self.num_total - self.num_used
	}

	pub fn used_pages(&self) -> uint {
		self.num_used
	}

	#[inline(never)]
	pub fn stat(&self) {
		write("Free: ");
		writeUInt(self.free_pages() * 4);
		write("KiB, Used: ");
		writeUInt(self.used_pages() * 4);
		write("KiB\n");
	}
}

#[inline(always)]
pub fn get() -> &mut Global {
	unsafe { &mut global }
}

impl PerCpu {
	pub fn new() -> PerCpu {
		PerCpu { free : none }
	}

	#[inline(never)]
	pub fn alloc_frame(&mut self) -> Option<*mut u8> {
		match pop_frame(&mut self.free) {
			Some(page) => { return Some(page as *mut u8); }
			None => {}
		}
		self.free = store(get().alloc_frame());
		return self.steal_frame();
	}

	pub fn steal_frame(&mut self) -> Option<*mut u8> {
		match get().alloc_frame() {
			Some(page) => Some(page as *mut u8),
			None => None
		}
	}

	pub fn alloc_frame_panic(&mut self) -> *mut u8 {
		match self.alloc_frame() {
			Some(page) => page,
			None => abort()
		}
	}

	pub fn free_frame(&mut self, page : *mut u8) {
		get().free_frame(page);
	}

	pub fn test(&mut self) {
		let mut head = none;
		let mut count = 0;
		loop {
			let p = self.alloc_frame();
//			write("Allocation #");
//			writeUInt(count);
//			write(": ");
//			writePtr(from_option(p, 0 as *mut u8) as *u8);
//			newline();
//			get().stat();
			match p {
				Some(pp) => push_frame(&mut head, pp),
				None => break
			}
			count += 1;
		}
		write("Allocated everything: ");
		writeUInt(count);
		write(" pages\n");
		get().stat();
		loop {
//			write("Allocation #");
//			writeUInt(count);
//			write(": ");
//			writePtr(from_option(head, 0 as *mut FreeFrame) as *u8);
//			newline();
//			get().stat();
			match pop_frame(&mut head) {
				Some(p) => self.free_frame(p as *mut u8),
				None => break
			}
			count -= 1;
		}
	}
}
<|endoftext|>