<commit_before>#![feature(unboxed_closures)]
#![feature(globs)]
#![feature(phase)]
#![feature(macro_rules)]
#[phase(plugin, link)]
extern crate log;
#[phase(plugin)]
extern crate wtftw_core;

extern crate wtftw_core;

use wtftw_core::window_system::*;
use wtftw_core::window_manager::*;
use wtftw_core::handlers::default::*;
use wtftw_core::config::*;
use wtftw_core::util::*;
use wtftw_core::layout::*;
use wtftw_core::layout::Direction::*;
use wtftw_core::layout::LayoutMessage::*;

#[no_mangle]
pub extern fn configure(_: &mut WindowManager, w: &WindowSystem, config: &mut Config) {
    let modm = MOD1MASK;

    config.general.mod_mask = modm;
    config.general.spacing = 0;
    config.general.border_color = 0x3f3f4c;
    config.general.focus_border_color = 0x525263;
    config.general.terminal = (String::from_str("urxvt"), String::from_str(""));
    config.general.layout = LayoutCollection::new(vec!(
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), ResizableTallLayout::new())),
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), MirrorLayout::new(ResizableTallLayout::new()))),
        box FullLayout));

    config.general.tags = (vec!("1: term", "2: web", "3: code",
                                "4: media", "5: steam", "6: latex",
                                "7: music", "8: im", "9: rest"))
        .into_iter().map(String::from_str).collect();

    // Register key handlers

    // Some standard key handlers for starting, restarting, etc.
    add_key_handler_str!(config, w, "q",      modm | SHIFTMASK, exit);
    add_key_handler_str!(config, w, "q",      modm,             restart);
    add_key_handler_str!(config, w, "Return", modm | SHIFTMASK, start_terminal);
    add_key_handler_str!(config, w, "p",      modm,             start_launcher);

    // Focus and window movement
    add_key_handler_str!(config, w, "j", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_down()));
    add_key_handler_str!(config, w, "k", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_up()));
    add_key_handler_str!(config, w, "j", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_down()));
    add_key_handler_str!(config, w, "k", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_up()));
    add_key_handler_str!(config, w, "Return", modm, |&: m, w, c| m.windows(w, c, |x| x.swap_master()));

    // Layout messages
    add_key_handler_str!(config, w, "h", modm,
            |&: m, w, c| {
                m.send_layout_message(LayoutMessage::Decrease)
                 .reapply_layout(w, c)
            });

    add_key_handler_str!(config, w, "l", modm,
            |&: m, w, c| {
                m.send_layout_message(LayoutMessage::Increase)
                 .reapply_layout(w, c)
            });

    add_key_handler_str!(config, w, "comma", modm,
            |&: m, w, c| {
                m.send_layout_message(LayoutMessage::IncreaseMaster)
                 .reapply_layout(w, c)
            });

    add_key_handler_str!(config, w, "period", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::DecreaseMaster).reapply_layout(w, c));
    add_key_handler_str!(config, w, "space", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::Next).reapply_layout(w, c));

    // Workspace switching and moving
    for i in range(1u, 10) {
        add_key_handler_str!(config, w, i.to_string().as_slice(), modm,
            move |&: m, w, c| switch_to_workspace(m, w, c, i - 1));

        add_key_handler_str!(config, w, i.to_string().as_slice(), modm | SHIFTMASK,
            move |&: m, w, c| move_window_to_workspace(m, w, c, i - 1));
    }

    // Media keys
    add_key_handler_str!(config, w, "j", modm | CONTROLMASK,
            |&: w, _, _| {
                run(String::from_str("amixer"), Some(String::from_str("-q set Master 5%-")));
                w
            });

    add_key_handler_str!(config, w, "k", modm | CONTROLMASK,
            |&: w, _, _| {
                run(String::from_str("amixer"), Some(String::from_str("-q set Master 5%+")));
                w
            });

    add_key_handler_code!(config, 0x1008ff11, NONEMASK,
            |&: w, _, _| {
                run(String::from_str("amixer"), Some(String::from_str("-q set Master 5%-")));
                w
            });
    add_key_handler_code!(config, 0x1008ff13, NONEMASK,
            |&: w, _, _| {
                run(String::from_str("amixer"), Some(String::from_str("-q set Master 5%+")));
                w
            });

    add_key_handler_code!(config, 0x1008ff02, NONEMASK,
            |&: w, _, _| {
                run(String::from_str("xbacklight"), Some(String::from_str("+10")));
                w
            });

    add_key_handler_code!(config, 0x1008ff03, NONEMASK,
            |&: w, _, _| {
                run(String::from_str("xbacklight"), Some(String::from_str("-10")));
                w
            });

    // Place specific applications on specific workspaces
    config.set_manage_hook(box |&: workspaces, window_system, window| {
                match window_system.get_class_name(window).as_slice() {
                    "MPlayer" => spawn_on(workspaces, window_system, window, 3),
                    "vlc"     => spawn_on(workspaces, window_system, window, 3),
                    _         => workspaces.clone()
                }
            });

    // Xmobar handling and formatting
    let mut xmobar = spawn_pipe(config, String::from_str("xmobar"),
                                Some(String::from_str("/home/wollwage/.xmonad/xmobar1.hs")));
    let tags = config.general.tags.clone();
    config.set_log_hook(box move |&mut: m, _| {
        let p = &mut xmobar;
        let tags = &tags;
        let workspaces = tags.clone().iter()
            .enumerate()
            .map(|(i, x)| if i as u32 == m.workspaces.current.workspace.id {
                format!("[<fc=#3279a8>{}</fc>] ", x)
            } else {
                format!("[{}] ", x)
            })
            .fold(String::from_str(""), |a, x| {
                let mut r = a.clone();
                r.push_str(x.as_slice());
                r
            });

        let content = format!("{} {}", workspaces, m.workspaces.current.workspace.layout.description());
        p.stdin.as_mut().unwrap().write_line(content.as_slice()).unwrap();
    });
}

<commit_msg>updated config<commit_after>#![feature(unboxed_closures)]
#![feature(globs)]
#![feature(phase)]
#![feature(macro_rules)]
#[phase(plugin, link)]
extern crate log;
#[phase(plugin)]
extern crate wtftw_core;

extern crate wtftw_core;

use wtftw_core::window_system::*;
use wtftw_core::window_manager::*;
use wtftw_core::handlers::default::*;
use wtftw_core::config::*;
use wtftw_core::util::*;
use wtftw_core::layout::*;
use wtftw_core::layout::Direction::*;
use wtftw_core::layout::LayoutMessage::*;

#[no_mangle]
pub extern fn configure(_: &mut WindowManager, w: &WindowSystem, config: &mut Config) {
    let modm = MOD1MASK;

    config.general.mod_mask = modm;
    config.general.spacing = 0;
    config.general.border_color = 0x3f3f4c;
    config.general.focus_border_color = 0x525263;
    config.general.terminal = (String::from_str("urxvt"), String::from_str(""));
    config.general.layout = LayoutCollection::new(vec!(
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), ResizableTallLayout::new())),
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), MirrorLayout::new(ResizableTallLayout::new()))),
        box FullLayout));

    config.general.tags = (vec!("1: term", "2: web", "3: code",
                                "4: media", "5: steam", "6: latex",
                                "7: music", "8: im", "9: rest"))
        .into_iter().map(String::from_str).collect();

    // Register key handlers

    // Some standard key handlers for starting, restarting, etc.
    add_key_handler_str!(config, w, "q",      modm | SHIFTMASK, exit);
    add_key_handler_str!(config, w, "q",      modm,             restart);
    add_key_handler_str!(config, w, "Return", modm | SHIFTMASK, start_terminal);
    add_key_handler_str!(config, w, "p",      modm,             start_launcher);

    // Focus and window movement
    add_key_handler_str!(config, w, "j", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_down()));
    add_key_handler_str!(config, w, "k", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_up()));
    add_key_handler_str!(config, w, "j", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_down()));
    add_key_handler_str!(config, w, "k", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_up()));
    add_key_handler_str!(config, w, "Return", modm, |&: m, w, c| m.windows(w, c, |x| x.swap_master()));

    // Layout messages
    add_key_handler_str!(config, w, "h", modm,
            |&: m, w, c| {
                m.send_layout_message(LayoutMessage::Decrease).reapply_layout(w, c)
            });

    add_key_handler_str!(config, w, "l", modm,
            |&: m, w, c| {
                m.send_layout_message(LayoutMessage::Increase).reapply_layout(w, c)
            });

    add_key_handler_str!(config, w, "comma", modm,
            |&: m, w, c| {
                m.send_layout_message(LayoutMessage::IncreaseMaster).reapply_layout(w, c)
            });

    add_key_handler_str!(config, w, "period", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::DecreaseMaster).reapply_layout(w, c));
    add_key_handler_str!(config, w, "space", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::Next).reapply_layout(w, c));

    // Workspace switching and moving
    for i in range(1u, 10) {
        add_key_handler_str!(config, w, i.to_string().as_slice(), modm,
            move |&: m, w, c| switch_to_workspace(m, w, c, i - 1));

        add_key_handler_str!(config, w, i.to_string().as_slice(), modm | SHIFTMASK,
            move |&: m, w, c| move_window_to_workspace(m, w, c, i - 1));
    }

    // Media keys
    add_key_handler_str!(config, w, "j", modm | CONTROLMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%-")); w });

    add_key_handler_str!(config, w, "k", modm | CONTROLMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%+")); w });

    add_key_handler_code!(config, 0x1008ff11, NONEMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%-")); w });
    add_key_handler_code!(config, 0x1008ff13, NONEMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%+")); w });

    add_key_handler_code!(config, 0x1008ff02, NONEMASK,
            |&: w, _, _| { run("xbacklight", Some("+10")); w });

    add_key_handler_code!(config, 0x1008ff03, NONEMASK,
            |&: w, _, _| { run("xbacklight", Some("-10")); w });

    // Place specific applications on specific workspaces
    config.set_manage_hook(box |&: workspaces, window_system, window| {
                match window_system.get_class_name(window).as_slice() {
                    "MPlayer" => spawn_on(workspaces, window_system, window, 3),
                    "vlc"     => spawn_on(workspaces, window_system, window, 3),
                    _         => workspaces.clone()
                }
            });

    // Xmobar handling and formatting
    let mut xmobar = spawn_pipe(config, String::from_str("xmobar"),
                                Some(String::from_str("/home/wollwage/.xmonad/xmobar1.hs")));
    let tags = config.general.tags.clone();
    config.set_log_hook(box move |&mut: m, _| {
        let p = &mut xmobar;
        let tags = &tags;
        let workspaces = tags.clone().iter()
            .enumerate()
            .map(|(i, x)| if i as u32 == m.workspaces.current.workspace.id {
                format!("[<fc=#3279a8>{}</fc>] ", x)
            } else {
                format!("[{}] ", x)
            })
            .fold(String::from_str(""), |a, x| {
                let mut r = a.clone();
                r.push_str(x.as_slice());
                r
            });

        let content = format!("{} {}", workspaces, m.workspaces.current.workspace.layout.description());
        p.stdin.as_mut().unwrap().write_line(content.as_slice()).unwrap();
    });
}

<|endoftext|>


<commit_before>use bson::Document;
use bson::Bson::{FloatingPoint, I64};
use bson::Bson::String as BsonString;
use mongodb::client::cursor::Cursor;
use mongodb::client::wire_protocol::flags::{OpInsertFlags, OpQueryFlags};
use mongodb::client::wire_protocol::operations::Message;
use std::io::Write;
use std::net::TcpStream;

#[test]
fn cursor_features() {
    match TcpStream::connect("localhost:27017") {
        Ok(mut stream) => {
            let docs : Vec<_> = (0..10).map(|i| {
                let mut doc = Document::new();
                doc.insert("foo".to_owned(), I64(i));

                doc
            }).collect();

            let flags = OpInsertFlags::no_flags();
            let name = "test.cursor_test".to_owned();
            let res = Message::with_insert(1, flags, name, docs);

            let cm = match res {
                Ok(message) => message,
                Err(_) => panic!("Could not create insert message!")
            };

            match cm.write(&mut stream) {
                Ok(_) => (),
                Err(s) => panic!("{}", s)
            };

            let doc = Document::new();
            let flags = OpQueryFlags::no_flags();
            let name = "test.test";
            let result = Cursor::query_with_batch_size(&mut stream, 3, 2, flags,
                                                       name, 0, 0, doc, None);

            let mut cursor = match result {
                Ok(c) => c,
                Err(s) => panic!("{}", s)
            };

            let batch = cursor.next_batch();

            assert_eq!(batch.len(), 3 as usize);

            for i in 0..batch.len() {
                match batch[i].get("foo") {
                    Some(&I64(j)) => assert_eq!(i as i64, j),
                    _ => panic!("Wrong value returned from Cursor#next_batch")
                };
            }

            let bson = match cursor.next() {
                Some(b) => b,
                None => panic!("Nothing returned from Cursor#next")
            };

            match bson.get("foo") {
                Some(&I64(3)) => (),
                _ => panic!("Wrong value returned from Cursor#next")
            };

            let vec = cursor.next_n(20);

            assert_eq!(vec.len(), 6 as usize);

            for i in 0..vec.len() {
                match vec[i].get("foo") {
                    Some(&I64(j)) => assert_eq!(4 + i as i64 , j),
                    _ => panic!("Wrong value returned from Cursor#next_batch")
                };
            }
        },
        Err(_) => {
            panic!("Could not connect to server")
        }
    }
}
<commit_msg>missed one collection name change<commit_after>use bson::Document;
use bson::Bson::{FloatingPoint, I64};
use bson::Bson::String as BsonString;
use mongodb::client::cursor::Cursor;
use mongodb::client::wire_protocol::flags::{OpInsertFlags, OpQueryFlags};
use mongodb::client::wire_protocol::operations::Message;
use std::io::Write;
use std::net::TcpStream;

#[test]
fn cursor_features() {
    match TcpStream::connect("localhost:27017") {
        Ok(mut stream) => {
            let docs : Vec<_> = (0..10).map(|i| {
                let mut doc = Document::new();
                doc.insert("foo".to_owned(), I64(i));

                doc
            }).collect();

            let flags = OpInsertFlags::no_flags();
            let name = "test.cursor_test".to_owned();
            let res = Message::with_insert(1, flags, name, docs);

            let cm = match res {
                Ok(message) => message,
                Err(_) => panic!("Could not create insert message!")
            };

            match cm.write(&mut stream) {
                Ok(_) => (),
                Err(s) => panic!("{}", s)
            };

            let doc = Document::new();
            let flags = OpQueryFlags::no_flags();
            let name = "test.cursor_test";
            let result = Cursor::query_with_batch_size(&mut stream, 3, 2, flags,
                                                       name, 0, 0, doc, None);

            let mut cursor = match result {
                Ok(c) => c,
                Err(s) => panic!("{}", s)
            };

            let batch = cursor.next_batch();

            assert_eq!(batch.len(), 3 as usize);

            for i in 0..batch.len() {
                match batch[i].get("foo") {
                    Some(&I64(j)) => assert_eq!(i as i64, j),
                    _ => panic!("Wrong value returned from Cursor#next_batch")
                };
            }

            let bson = match cursor.next() {
                Some(b) => b,
                None => panic!("Nothing returned from Cursor#next")
            };

            match bson.get("foo") {
                Some(&I64(3)) => (),
                _ => panic!("Wrong value returned from Cursor#next")
            };

            let vec = cursor.next_n(20);

            assert_eq!(vec.len(), 6 as usize);

            for i in 0..vec.len() {
                match vec[i].get("foo") {
                    Some(&I64(j)) => assert_eq!(4 + i as i64 , j),
                    _ => panic!("Wrong value returned from Cursor#next_batch")
                };
            }
        },
        Err(_) => {
            panic!("Could not connect to server")
        }
    }
}
<|endoftext|>


<commit_before>use super::{Error, TagKind, TagsConfiguration, TagsContext};
use std::collections::HashMap;
use std::ffi::CStr;
use std::process::abort;
use std::{fmt, slice, str};
use tree_sitter::Language;

#[repr(C)]
#[derive(Debug, PartialEq, Eq)]
pub enum TSTagsError {
    Ok,
    UnknownScope,
    Timeout,
    InvalidLanguage,
    InvalidUtf8,
    InvalidRegex,
    InvalidQuery,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TSTagKind {
    Function,
    Method,
    Class,
    Module,
    Call,
}

#[repr(C)]
pub struct TSPoint {
    row: u32,
    column: u32,
}

#[repr(C)]
pub struct TSTag {
    pub kind: TSTagKind,
    pub start_byte: u32,
    pub end_byte: u32,
    pub name_start_byte: u32,
    pub name_end_byte: u32,
    pub line_start_byte: u32,
    pub line_end_byte: u32,
    pub start_point: TSPoint,
    pub end_point: TSPoint,
    pub docs_start_byte: u32,
    pub docs_end_byte: u32,
}

pub struct TSTagger {
    languages: HashMap<String, TagsConfiguration>,
}

pub struct TSTagsBuffer {
    context: TagsContext,
    tags: Vec<TSTag>,
    docs: Vec<u8>,
}

#[no_mangle]
pub extern "C" fn ts_tagger_new() -> *mut TSTagger {
    Box::into_raw(Box::new(TSTagger {
        languages: HashMap::new(),
    }))
}

pub extern "C" fn ts_tagger_delete(this: *mut TSTagger) {
    drop(unsafe { Box::from_raw(this) })
}

#[no_mangle]
pub extern "C" fn ts_tagger_add_language(
    this: *mut TSTagger,
    scope_name: *const i8,
    language: Language,
    tags_query: *const u8,
    locals_query: *const u8,
    tags_query_len: u32,
    locals_query_len: u32,
) -> TSTagsError {
    let tagger = unwrap_mut_ptr(this);
    let scope_name = unsafe { unwrap(CStr::from_ptr(scope_name).to_str()) };
    let tags_query = unsafe { slice::from_raw_parts(tags_query, tags_query_len as usize) };
    let locals_query = unsafe { slice::from_raw_parts(locals_query, locals_query_len as usize) };
    let tags_query = match str::from_utf8(tags_query) {
        Ok(e) => e,
        Err(_) => return TSTagsError::InvalidUtf8,
    };
    let locals_query = match str::from_utf8(locals_query) {
        Ok(e) => e,
        Err(_) => return TSTagsError::InvalidUtf8,
    };
    match TagsConfiguration::new(language, tags_query, locals_query) {
        Ok(c) => {
            tagger.languages.insert(scope_name.to_string(), c);
            TSTagsError::Ok
        }
        Err(Error::Query(_)) => TSTagsError::InvalidQuery,
        Err(Error::Regex(_)) => TSTagsError::InvalidRegex,
    }
}

#[no_mangle]
pub extern "C" fn ts_tagger_tag(
    this: *mut TSTagger,
    scope_name: *const i8,
    source_code: *const u8,
    source_code_len: u32,
    output: *mut TSTagsBuffer,
    cancellation_flag: *const usize,
) -> TSTagsError {
    let tagger = unwrap_mut_ptr(this);
    let buffer = unwrap_mut_ptr(output);
    let scope_name = unsafe { unwrap(CStr::from_ptr(scope_name).to_str()) };
    if let Some(config) = tagger.languages.get(scope_name) {
        buffer.tags.clear();
        buffer.docs.clear();
        let source_code = unsafe { slice::from_raw_parts(source_code, source_code_len as usize) };

        for tag in buffer.context.generate_tags(config, source_code) {
            let prev_docs_len = buffer.docs.len();
            if let Some(docs) = tag.docs {
                buffer.docs.extend_from_slice(docs.as_bytes());
            }
            buffer.tags.push(TSTag {
                kind: match tag.kind {
                    TagKind::Function => TSTagKind::Function,
                    TagKind::Method => TSTagKind::Method,
                    TagKind::Class => TSTagKind::Class,
                    TagKind::Module => TSTagKind::Module,
                    TagKind::Call => TSTagKind::Call,
                },
                start_byte: tag.range.start as u32,
                end_byte: tag.range.end as u32,
                name_start_byte: tag.name_range.start as u32,
                name_end_byte: tag.name_range.end as u32,
                line_start_byte: tag.line_range.start as u32,
                line_end_byte: tag.line_range.end as u32,
                start_point: TSPoint {
                    row: tag.span.start.row as u32,
                    column: tag.span.start.column as u32,
                },
                end_point: TSPoint {
                    row: tag.span.end.row as u32,
                    column: tag.span.end.column as u32,
                },
                docs_start_byte: prev_docs_len as u32,
                docs_end_byte: buffer.docs.len() as u32,
            });
        }

        TSTagsError::Ok
    } else {
        TSTagsError::UnknownScope
    }
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_new() -> *mut TSTagsBuffer {
    Box::into_raw(Box::new(TSTagsBuffer {
        context: TagsContext::new(),
        tags: Vec::new(),
        docs: Vec::new(),
    }))
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_delete(this: *mut TSTagsBuffer) {
    drop(unsafe { Box::from_raw(this) })
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_tags(this: *const TSTagsBuffer) -> *const TSTag {
    let buffer = unwrap_ptr(this);
    buffer.tags.as_ptr()
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_tags_len(this: *const TSTagsBuffer) -> u32 {
    let buffer = unwrap_ptr(this);
    buffer.tags.len() as u32
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_docs(this: *const TSTagsBuffer) -> *const i8 {
    let buffer = unwrap_ptr(this);
    buffer.docs.as_ptr() as *const i8
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_docs_len(this: *const TSTagsBuffer) -> u32 {
    let buffer = unwrap_ptr(this);
    buffer.docs.len() as u32
}

fn unwrap_ptr<'a, T>(result: *const T) -> &'a T {
    unsafe { result.as_ref() }.unwrap_or_else(|| {
        eprintln!("{}:{} - pointer must not be null", file!(), line!());
        abort();
    })
}

fn unwrap_mut_ptr<'a, T>(result: *mut T) -> &'a mut T {
    unsafe { result.as_mut() }.unwrap_or_else(|| {
        eprintln!("{}:{} - pointer must not be null", file!(), line!());
        abort();
    })
}

fn unwrap<T, E: fmt::Display>(result: Result<T, E>) -> T {
    result.unwrap_or_else(|error| {
        eprintln!("tree-sitter tag error: {}", error);
        abort();
    })
}
<commit_msg>Add missing no_mangle attribute on ts_tagger_delete<commit_after>use super::{Error, TagKind, TagsConfiguration, TagsContext};
use std::collections::HashMap;
use std::ffi::CStr;
use std::process::abort;
use std::{fmt, slice, str};
use tree_sitter::Language;

#[repr(C)]
#[derive(Debug, PartialEq, Eq)]
pub enum TSTagsError {
    Ok,
    UnknownScope,
    Timeout,
    InvalidLanguage,
    InvalidUtf8,
    InvalidRegex,
    InvalidQuery,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TSTagKind {
    Function,
    Method,
    Class,
    Module,
    Call,
}

#[repr(C)]
pub struct TSPoint {
    row: u32,
    column: u32,
}

#[repr(C)]
pub struct TSTag {
    pub kind: TSTagKind,
    pub start_byte: u32,
    pub end_byte: u32,
    pub name_start_byte: u32,
    pub name_end_byte: u32,
    pub line_start_byte: u32,
    pub line_end_byte: u32,
    pub start_point: TSPoint,
    pub end_point: TSPoint,
    pub docs_start_byte: u32,
    pub docs_end_byte: u32,
}

pub struct TSTagger {
    languages: HashMap<String, TagsConfiguration>,
}

pub struct TSTagsBuffer {
    context: TagsContext,
    tags: Vec<TSTag>,
    docs: Vec<u8>,
}

#[no_mangle]
pub extern "C" fn ts_tagger_new() -> *mut TSTagger {
    Box::into_raw(Box::new(TSTagger {
        languages: HashMap::new(),
    }))
}

#[no_mangle]
pub extern "C" fn ts_tagger_delete(this: *mut TSTagger) {
    drop(unsafe { Box::from_raw(this) })
}

#[no_mangle]
pub extern "C" fn ts_tagger_add_language(
    this: *mut TSTagger,
    scope_name: *const i8,
    language: Language,
    tags_query: *const u8,
    locals_query: *const u8,
    tags_query_len: u32,
    locals_query_len: u32,
) -> TSTagsError {
    let tagger = unwrap_mut_ptr(this);
    let scope_name = unsafe { unwrap(CStr::from_ptr(scope_name).to_str()) };
    let tags_query = unsafe { slice::from_raw_parts(tags_query, tags_query_len as usize) };
    let locals_query = unsafe { slice::from_raw_parts(locals_query, locals_query_len as usize) };
    let tags_query = match str::from_utf8(tags_query) {
        Ok(e) => e,
        Err(_) => return TSTagsError::InvalidUtf8,
    };
    let locals_query = match str::from_utf8(locals_query) {
        Ok(e) => e,
        Err(_) => return TSTagsError::InvalidUtf8,
    };
    match TagsConfiguration::new(language, tags_query, locals_query) {
        Ok(c) => {
            tagger.languages.insert(scope_name.to_string(), c);
            TSTagsError::Ok
        }
        Err(Error::Query(_)) => TSTagsError::InvalidQuery,
        Err(Error::Regex(_)) => TSTagsError::InvalidRegex,
    }
}

#[no_mangle]
pub extern "C" fn ts_tagger_tag(
    this: *mut TSTagger,
    scope_name: *const i8,
    source_code: *const u8,
    source_code_len: u32,
    output: *mut TSTagsBuffer,
    cancellation_flag: *const usize,
) -> TSTagsError {
    let tagger = unwrap_mut_ptr(this);
    let buffer = unwrap_mut_ptr(output);
    let scope_name = unsafe { unwrap(CStr::from_ptr(scope_name).to_str()) };
    if let Some(config) = tagger.languages.get(scope_name) {
        buffer.tags.clear();
        buffer.docs.clear();
        let source_code = unsafe { slice::from_raw_parts(source_code, source_code_len as usize) };

        for tag in buffer.context.generate_tags(config, source_code) {
            let prev_docs_len = buffer.docs.len();
            if let Some(docs) = tag.docs {
                buffer.docs.extend_from_slice(docs.as_bytes());
            }
            buffer.tags.push(TSTag {
                kind: match tag.kind {
                    TagKind::Function => TSTagKind::Function,
                    TagKind::Method => TSTagKind::Method,
                    TagKind::Class => TSTagKind::Class,
                    TagKind::Module => TSTagKind::Module,
                    TagKind::Call => TSTagKind::Call,
                },
                start_byte: tag.range.start as u32,
                end_byte: tag.range.end as u32,
                name_start_byte: tag.name_range.start as u32,
                name_end_byte: tag.name_range.end as u32,
                line_start_byte: tag.line_range.start as u32,
                line_end_byte: tag.line_range.end as u32,
                start_point: TSPoint {
                    row: tag.span.start.row as u32,
                    column: tag.span.start.column as u32,
                },
                end_point: TSPoint {
                    row: tag.span.end.row as u32,
                    column: tag.span.end.column as u32,
                },
                docs_start_byte: prev_docs_len as u32,
                docs_end_byte: buffer.docs.len() as u32,
            });
        }

        TSTagsError::Ok
    } else {
        TSTagsError::UnknownScope
    }
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_new() -> *mut TSTagsBuffer {
    Box::into_raw(Box::new(TSTagsBuffer {
        context: TagsContext::new(),
        tags: Vec::new(),
        docs: Vec::new(),
    }))
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_delete(this: *mut TSTagsBuffer) {
    drop(unsafe { Box::from_raw(this) })
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_tags(this: *const TSTagsBuffer) -> *const TSTag {
    let buffer = unwrap_ptr(this);
    buffer.tags.as_ptr()
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_tags_len(this: *const TSTagsBuffer) -> u32 {
    let buffer = unwrap_ptr(this);
    buffer.tags.len() as u32
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_docs(this: *const TSTagsBuffer) -> *const i8 {
    let buffer = unwrap_ptr(this);
    buffer.docs.as_ptr() as *const i8
}

#[no_mangle]
pub extern "C" fn ts_tags_buffer_docs_len(this: *const TSTagsBuffer) -> u32 {
    let buffer = unwrap_ptr(this);
    buffer.docs.len() as u32
}

fn unwrap_ptr<'a, T>(result: *const T) -> &'a T {
    unsafe { result.as_ref() }.unwrap_or_else(|| {
        eprintln!("{}:{} - pointer must not be null", file!(), line!());
        abort();
    })
}

fn unwrap_mut_ptr<'a, T>(result: *mut T) -> &'a mut T {
    unsafe { result.as_mut() }.unwrap_or_else(|| {
        eprintln!("{}:{} - pointer must not be null", file!(), line!());
        abort();
    })
}

fn unwrap<T, E: fmt::Display>(result: Result<T, E>) -> T {
    result.unwrap_or_else(|error| {
        eprintln!("tree-sitter tag error: {}", error);
        abort();
    })
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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 std::borrow::Cow;

use syntax::codemap::{BytePos, FileName, Pos, Span};

use codemap::LineRangeUtils;
use comment::{rewrite_comment, CodeCharKind, CommentCodeSlices};
use config::WriteMode;
use shape::{Indent, Shape};
use utils::{count_newlines, last_line_width, mk_sp};
use visitor::FmtVisitor;

struct SnippetStatus {
    /// An offset to the current line from the beginnig of the original snippet.
    line_start: usize,
    /// A length of trailing whitespaces on the current line.
    last_wspace: Option<usize>,
    /// The current line number.
    cur_line: usize,
}

impl SnippetStatus {
    fn new(cur_line: usize) -> Self {
        SnippetStatus {
            line_start: 0,
            last_wspace: None,
            cur_line,
        }
    }
}

impl<'a> FmtVisitor<'a> {
    fn output_at_start(&self) -> bool {
        self.buffer.is_empty()
    }

    // TODO these format_missing methods are ugly. Refactor and add unit tests
    // for the central whitespace stripping loop.
    pub fn format_missing(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| this.push_str(last_snippet))
    }

    pub fn format_missing_with_indent(&mut self, end: BytePos) {
        let config = self.config;
        self.format_missing_inner(end, |this, last_snippet, snippet| {
            this.push_str(last_snippet.trim_right());
            if last_snippet == snippet && !this.output_at_start() {
                // No new lines in the snippet.
                this.push_str("\n");
            }
            let indent = this.block_indent.to_string(config);
            this.push_str(&indent);
        })
    }

    pub fn format_missing_no_indent(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| {
            this.push_str(last_snippet.trim_right());
        })
    }

    fn format_missing_inner<F: Fn(&mut FmtVisitor, &str, &str)>(
        &mut self,
        end: BytePos,
        process_last_snippet: F,
    ) {
        let start = self.last_pos;

        if start == end {
            // Do nothing if this is the beginning of the file.
            if !self.output_at_start() {
                process_last_snippet(self, "", "");
            }
            return;
        }

        assert!(
            start < end,
            "Request to format inverted span: {:?} to {:?}",
            self.codemap.lookup_char_pos(start),
            self.codemap.lookup_char_pos(end)
        );

        self.last_pos = end;
        let span = mk_sp(start, end);
        let snippet = self.snippet(span);
        if snippet.trim().is_empty() && !out_of_file_lines_range!(self, span) {
            // Keep vertical spaces within range.
            self.push_vertical_spaces(count_newlines(snippet));
            process_last_snippet(self, "", snippet);
        } else {
            self.write_snippet(span, &process_last_snippet);
        }
    }

    fn push_vertical_spaces(&mut self, mut newline_count: usize) {
        let offset = self.count_trailing_newlines();
        let newline_upper_bound = self.config.blank_lines_upper_bound() + 1;
        let newline_lower_bound = self.config.blank_lines_lower_bound() + 1;

        if newline_count + offset > newline_upper_bound {
            if offset >= newline_upper_bound {
                newline_count = 0;
            } else {
                newline_count = newline_upper_bound - offset;
            }
        } else if newline_count + offset < newline_lower_bound {
            if offset >= newline_lower_bound {
                newline_count = 0;
            } else {
                newline_count = newline_lower_bound - offset;
            }
        }

        let blank_lines = "\n".repeat(newline_count);
        self.push_str(&blank_lines);
    }

    fn count_trailing_newlines(&self) -> usize {
        let mut buf = &*self.buffer;
        let mut result = 0;
        while buf.ends_with('\n') {
            buf = &buf[..buf.len() - 1];
            result += 1;
        }
        result
    }

    fn write_snippet<F>(&mut self, span: Span, process_last_snippet: F)
    where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Get a snippet from the file start to the span's hi without allocating.
        // We need it to determine what precedes the current comment. If the comment
        // follows code on the same line, we won't touch it.
        let big_span_lo = self.codemap.lookup_char_pos(span.lo()).file.start_pos;
        let local_begin = self.codemap.lookup_byte_offset(big_span_lo);
        let local_end = self.codemap.lookup_byte_offset(span.hi());
        let start_index = local_begin.pos.to_usize();
        let end_index = local_end.pos.to_usize();
        let big_snippet = &local_begin.fm.src.as_ref().unwrap()[start_index..end_index];

        let big_diff = (span.lo() - big_span_lo).to_usize();
        let snippet = self.snippet(span);

        debug!("write_snippet `{}`", snippet);

        self.write_snippet_inner(big_snippet, big_diff, snippet, span, process_last_snippet);
    }

    fn write_snippet_inner<F>(
        &mut self,
        big_snippet: &str,
        big_diff: usize,
        old_snippet: &str,
        span: Span,
        process_last_snippet: F,
    ) where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Trim whitespace from the right hand side of each line.
        // Annoyingly, the library functions for splitting by lines etc. are not
        // quite right, so we must do it ourselves.
        let char_pos = self.codemap.lookup_char_pos(span.lo());
        let file_name = &char_pos.file.name;
        let mut status = SnippetStatus::new(char_pos.line);

        let snippet = &*match self.config.write_mode() {
            WriteMode::Coverage => replace_chars(old_snippet),
            _ => Cow::from(old_snippet),
        };

        for (kind, offset, subslice) in CommentCodeSlices::new(snippet) {
            debug!("{:?}: {:?}", kind, subslice);

            let newline_count = count_newlines(subslice);
            let within_file_lines_range = self.config.file_lines().intersects_range(
                file_name,
                status.cur_line,
                status.cur_line + newline_count,
            );

            if CodeCharKind::Comment == kind && within_file_lines_range {
                // 1: comment.
                self.process_comment(
                    &mut status,
                    snippet,
                    &big_snippet[..(offset + big_diff)],
                    offset,
                    subslice,
                );
            } else if subslice.trim().is_empty() && newline_count > 0 && within_file_lines_range {
                // 2: blank lines.
                self.push_vertical_spaces(newline_count);
                status.cur_line += newline_count;
                status.line_start = offset + newline_count;
            } else {
                // 3: code which we failed to format or which is not within file-lines range.
                self.process_missing_code(&mut status, snippet, subslice, offset, file_name);
            }
        }

        process_last_snippet(self, &snippet[status.line_start..], snippet);
    }

    fn process_comment(
        &mut self,
        status: &mut SnippetStatus,
        snippet: &str,
        big_snippet: &str,
        offset: usize,
        subslice: &str,
    ) {
        let last_char = big_snippet
            .chars()
            .rev()
            .skip_while(|rev_c| [' ', '\t'].contains(rev_c))
            .next();

        let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c));

        let comment_indent = if fix_indent {
            if let Some('{') = last_char {
                self.push_str("\n");
            }
            let indent_str = self.block_indent.to_string(self.config);
            self.push_str(&indent_str);
            self.block_indent
        } else {
            self.push_str(" ");
            Indent::from_width(self.config, last_line_width(&self.buffer))
        };

        let comment_width = ::std::cmp::min(
            self.config.comment_width(),
            self.config.max_width() - self.block_indent.width(),
        );
        let comment_shape = Shape::legacy(comment_width, comment_indent);
        let comment_str = rewrite_comment(subslice, false, comment_shape, self.config)
            .unwrap_or_else(|| String::from(subslice));
        self.push_str(&comment_str);

        status.last_wspace = None;
        status.line_start = offset + subslice.len();

        if let Some('/') = subslice.chars().nth(1) {
            // check that there are no contained block comments
            if !subslice
                .split('\n')
                .map(|s| s.trim_left())
                .any(|s| s.len() >= 2 && &s[0..2] == "/*")
            {
                // Add a newline after line comments
                self.push_str("\n");
            }
        } else if status.line_start <= snippet.len() {
            // For other comments add a newline if there isn't one at the end already
            match snippet[status.line_start..].chars().next() {
                Some('\n') | Some('\r') => (),
                _ => self.push_str("\n"),
            }
        }

        status.cur_line += count_newlines(subslice);
    }

    fn process_missing_code(
        &mut self,
        status: &mut SnippetStatus,
        snippet: &str,
        subslice: &str,
        offset: usize,
        file_name: &FileName,
    ) {
        for (mut i, c) in subslice.char_indices() {
            i += offset;

            if c == '\n' {
                let skip_this_line = !self.config
                    .file_lines()
                    .contains_line(file_name, status.cur_line);
                if skip_this_line {
                    status.last_wspace = None;
                }

                if let Some(lw) = status.last_wspace {
                    self.push_str(&snippet[status.line_start..lw]);
                    self.push_str("\n");
                    status.last_wspace = None;
                } else {
                    self.push_str(&snippet[status.line_start..i + 1]);
                }

                status.cur_line += 1;
                status.line_start = i + 1;
            } else if c.is_whitespace() && status.last_wspace.is_none() {
                status.last_wspace = Some(i);
            } else if c == ';' && status.last_wspace.is_some() {
                status.line_start = i;
                status.last_wspace = None;
            } else {
                status.last_wspace = None;
            }
        }

        let remaining = snippet[status.line_start..subslice.len() + offset].trim();
        if !remaining.is_empty() {
            self.push_str(remaining);
            status.line_start = subslice.len() + offset;
        }
    }
}

fn replace_chars(string: &str) -> Cow<str> {
    Cow::from(
        string
            .chars()
            .map(|ch| if ch.is_whitespace() { ch } else { 'X' })
            .collect::<String>(),
    )
}
<commit_msg>Return String instead of always returing Cow::Owned<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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 std::borrow::Cow;

use syntax::codemap::{BytePos, FileName, Pos, Span};

use codemap::LineRangeUtils;
use comment::{rewrite_comment, CodeCharKind, CommentCodeSlices};
use config::WriteMode;
use shape::{Indent, Shape};
use utils::{count_newlines, last_line_width, mk_sp};
use visitor::FmtVisitor;

struct SnippetStatus {
    /// An offset to the current line from the beginnig of the original snippet.
    line_start: usize,
    /// A length of trailing whitespaces on the current line.
    last_wspace: Option<usize>,
    /// The current line number.
    cur_line: usize,
}

impl SnippetStatus {
    fn new(cur_line: usize) -> Self {
        SnippetStatus {
            line_start: 0,
            last_wspace: None,
            cur_line,
        }
    }
}

impl<'a> FmtVisitor<'a> {
    fn output_at_start(&self) -> bool {
        self.buffer.is_empty()
    }

    // TODO these format_missing methods are ugly. Refactor and add unit tests
    // for the central whitespace stripping loop.
    pub fn format_missing(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| this.push_str(last_snippet))
    }

    pub fn format_missing_with_indent(&mut self, end: BytePos) {
        let config = self.config;
        self.format_missing_inner(end, |this, last_snippet, snippet| {
            this.push_str(last_snippet.trim_right());
            if last_snippet == snippet && !this.output_at_start() {
                // No new lines in the snippet.
                this.push_str("\n");
            }
            let indent = this.block_indent.to_string(config);
            this.push_str(&indent);
        })
    }

    pub fn format_missing_no_indent(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| {
            this.push_str(last_snippet.trim_right());
        })
    }

    fn format_missing_inner<F: Fn(&mut FmtVisitor, &str, &str)>(
        &mut self,
        end: BytePos,
        process_last_snippet: F,
    ) {
        let start = self.last_pos;

        if start == end {
            // Do nothing if this is the beginning of the file.
            if !self.output_at_start() {
                process_last_snippet(self, "", "");
            }
            return;
        }

        assert!(
            start < end,
            "Request to format inverted span: {:?} to {:?}",
            self.codemap.lookup_char_pos(start),
            self.codemap.lookup_char_pos(end)
        );

        self.last_pos = end;
        let span = mk_sp(start, end);
        let snippet = self.snippet(span);
        if snippet.trim().is_empty() && !out_of_file_lines_range!(self, span) {
            // Keep vertical spaces within range.
            self.push_vertical_spaces(count_newlines(snippet));
            process_last_snippet(self, "", snippet);
        } else {
            self.write_snippet(span, &process_last_snippet);
        }
    }

    fn push_vertical_spaces(&mut self, mut newline_count: usize) {
        let offset = self.count_trailing_newlines();
        let newline_upper_bound = self.config.blank_lines_upper_bound() + 1;
        let newline_lower_bound = self.config.blank_lines_lower_bound() + 1;

        if newline_count + offset > newline_upper_bound {
            if offset >= newline_upper_bound {
                newline_count = 0;
            } else {
                newline_count = newline_upper_bound - offset;
            }
        } else if newline_count + offset < newline_lower_bound {
            if offset >= newline_lower_bound {
                newline_count = 0;
            } else {
                newline_count = newline_lower_bound - offset;
            }
        }

        let blank_lines = "\n".repeat(newline_count);
        self.push_str(&blank_lines);
    }

    fn count_trailing_newlines(&self) -> usize {
        let mut buf = &*self.buffer;
        let mut result = 0;
        while buf.ends_with('\n') {
            buf = &buf[..buf.len() - 1];
            result += 1;
        }
        result
    }

    fn write_snippet<F>(&mut self, span: Span, process_last_snippet: F)
    where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Get a snippet from the file start to the span's hi without allocating.
        // We need it to determine what precedes the current comment. If the comment
        // follows code on the same line, we won't touch it.
        let big_span_lo = self.codemap.lookup_char_pos(span.lo()).file.start_pos;
        let local_begin = self.codemap.lookup_byte_offset(big_span_lo);
        let local_end = self.codemap.lookup_byte_offset(span.hi());
        let start_index = local_begin.pos.to_usize();
        let end_index = local_end.pos.to_usize();
        let big_snippet = &local_begin.fm.src.as_ref().unwrap()[start_index..end_index];

        let big_diff = (span.lo() - big_span_lo).to_usize();
        let snippet = self.snippet(span);

        debug!("write_snippet `{}`", snippet);

        self.write_snippet_inner(big_snippet, big_diff, snippet, span, process_last_snippet);
    }

    fn write_snippet_inner<F>(
        &mut self,
        big_snippet: &str,
        big_diff: usize,
        old_snippet: &str,
        span: Span,
        process_last_snippet: F,
    ) where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Trim whitespace from the right hand side of each line.
        // Annoyingly, the library functions for splitting by lines etc. are not
        // quite right, so we must do it ourselves.
        let char_pos = self.codemap.lookup_char_pos(span.lo());
        let file_name = &char_pos.file.name;
        let mut status = SnippetStatus::new(char_pos.line);

        let snippet = &*match self.config.write_mode() {
            WriteMode::Coverage => Cow::from(replace_chars(old_snippet)),
            _ => Cow::from(old_snippet),
        };

        for (kind, offset, subslice) in CommentCodeSlices::new(snippet) {
            debug!("{:?}: {:?}", kind, subslice);

            let newline_count = count_newlines(subslice);
            let within_file_lines_range = self.config.file_lines().intersects_range(
                file_name,
                status.cur_line,
                status.cur_line + newline_count,
            );

            if CodeCharKind::Comment == kind && within_file_lines_range {
                // 1: comment.
                self.process_comment(
                    &mut status,
                    snippet,
                    &big_snippet[..(offset + big_diff)],
                    offset,
                    subslice,
                );
            } else if subslice.trim().is_empty() && newline_count > 0 && within_file_lines_range {
                // 2: blank lines.
                self.push_vertical_spaces(newline_count);
                status.cur_line += newline_count;
                status.line_start = offset + newline_count;
            } else {
                // 3: code which we failed to format or which is not within file-lines range.
                self.process_missing_code(&mut status, snippet, subslice, offset, file_name);
            }
        }

        process_last_snippet(self, &snippet[status.line_start..], snippet);
    }

    fn process_comment(
        &mut self,
        status: &mut SnippetStatus,
        snippet: &str,
        big_snippet: &str,
        offset: usize,
        subslice: &str,
    ) {
        let last_char = big_snippet
            .chars()
            .rev()
            .skip_while(|rev_c| [' ', '\t'].contains(rev_c))
            .next();

        let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c));

        let comment_indent = if fix_indent {
            if let Some('{') = last_char {
                self.push_str("\n");
            }
            let indent_str = self.block_indent.to_string(self.config);
            self.push_str(&indent_str);
            self.block_indent
        } else {
            self.push_str(" ");
            Indent::from_width(self.config, last_line_width(&self.buffer))
        };

        let comment_width = ::std::cmp::min(
            self.config.comment_width(),
            self.config.max_width() - self.block_indent.width(),
        );
        let comment_shape = Shape::legacy(comment_width, comment_indent);
        let comment_str = rewrite_comment(subslice, false, comment_shape, self.config)
            .unwrap_or_else(|| String::from(subslice));
        self.push_str(&comment_str);

        status.last_wspace = None;
        status.line_start = offset + subslice.len();

        if let Some('/') = subslice.chars().nth(1) {
            // check that there are no contained block comments
            if !subslice
                .split('\n')
                .map(|s| s.trim_left())
                .any(|s| s.len() >= 2 && &s[0..2] == "/*")
            {
                // Add a newline after line comments
                self.push_str("\n");
            }
        } else if status.line_start <= snippet.len() {
            // For other comments add a newline if there isn't one at the end already
            match snippet[status.line_start..].chars().next() {
                Some('\n') | Some('\r') => (),
                _ => self.push_str("\n"),
            }
        }

        status.cur_line += count_newlines(subslice);
    }

    fn process_missing_code(
        &mut self,
        status: &mut SnippetStatus,
        snippet: &str,
        subslice: &str,
        offset: usize,
        file_name: &FileName,
    ) {
        for (mut i, c) in subslice.char_indices() {
            i += offset;

            if c == '\n' {
                let skip_this_line = !self.config
                    .file_lines()
                    .contains_line(file_name, status.cur_line);
                if skip_this_line {
                    status.last_wspace = None;
                }

                if let Some(lw) = status.last_wspace {
                    self.push_str(&snippet[status.line_start..lw]);
                    self.push_str("\n");
                    status.last_wspace = None;
                } else {
                    self.push_str(&snippet[status.line_start..i + 1]);
                }

                status.cur_line += 1;
                status.line_start = i + 1;
            } else if c.is_whitespace() && status.last_wspace.is_none() {
                status.last_wspace = Some(i);
            } else if c == ';' && status.last_wspace.is_some() {
                status.line_start = i;
                status.last_wspace = None;
            } else {
                status.last_wspace = None;
            }
        }

        let remaining = snippet[status.line_start..subslice.len() + offset].trim();
        if !remaining.is_empty() {
            self.push_str(remaining);
            status.line_start = subslice.len() + offset;
        }
    }
}

fn replace_chars(string: &str) -> String {
    string
        .chars()
        .map(|ch| if ch.is_whitespace() { ch } else { 'X' })
        .collect()
}
<|endoftext|>


<commit_before>use payload::*;
use device_token::*;

pub struct Notification<'a> {
    // The Remote Notification Payload.
    pub payload: Payload,

    // Specify the hexadecimal string of the device token for the target device.
    pub device_token: DeviceToken,

    // The optional settings for the notification
    pub options: NotificationOptions<'a>,
}

pub struct NotificationOptions<'a> {
    // A canonical UUID that identifies the notification.
    pub apns_id: Option<&'a str>,

    // A UNIX epoch date expressed in seconds (UTC).
    pub apns_expiration: Option<i64>,

    // The priority of the notification.
    pub apns_priority: Option<u32>,

    // The topic of the remote notification, which is typically the bundle ID for your app.
    pub apns_topic: Option<&'a str>,
}

impl<'a> Default for NotificationOptions<'a> {
    fn default() -> NotificationOptions<'a> {
        NotificationOptions {
            apns_id: None,
            apns_expiration: None,
            apns_priority: None,
            apns_topic: None,
        }
    }
}

impl<'a> Notification<'a> {
    pub fn new(payload: Payload, token: DeviceToken, options: NotificationOptions) -> Notification {
        Notification {
            payload: payload,
            device_token: token,
            options: options,
        }
    }
}
<commit_msg>Changes in notifications<commit_after>use payload::*;
use device_token::*;

/// The Remote Notification.
pub struct Notification<'a> {
    /// The Remote Notification Payload.
    pub payload: Payload<'a>,

    /// Specify the hexadecimal string of the device token for the target device.
    pub device_token: DeviceToken<'a>,

    /// The optional settings for the notification
    pub options: NotificationOptions<'a>,
}

impl<'a> Notification<'a> {
    pub fn new(payload: Payload<'a>, token: DeviceToken<'a>, options: NotificationOptions<'a>) -> Notification<'a> {
        Notification {
            payload: payload,
            device_token: token,
            options: options,
        }
    }
}

/// Request headers.
pub struct NotificationOptions<'a> {
    /// A canonical UUID that identifies the notification.
    pub apns_id: Option<&'a str>,

    /// A UNIX epoch date expressed in seconds (UTC).
    pub apns_expiration: Option<i64>,

    /// The priority of the notification.
    pub apns_priority: Option<u32>,

    /// The topic of the remote notification, which is typically the bundle ID for your app.
    pub apns_topic: Option<&'a str>,
}

impl<'a> Default for NotificationOptions<'a> {
    fn default() -> NotificationOptions<'a> {
        NotificationOptions {
            apns_id: None,
            apns_expiration: None,
            apns_priority: None,
            apns_topic: None,
        }
    }
}
<|endoftext|>


<commit_before>use index::btree::dump::dump_tree;
use index::btree::external::ExtNode;
use index::btree::internal::InNode;
use index::btree::node::is_node_locked;
use index::btree::node::read_node;
use index::btree::node::read_unchecked;
use index::btree::node::write_node;
use index::btree::node::write_non_empty;
use index::btree::node::write_targeted;
use index::btree::node::EmptyNode;
use index::btree::node::Node;
use index::btree::node::NodeData;
use index::btree::node::NodeWriteGuard;
use index::btree::search::mut_search;
use index::btree::search::MutSearchResult;
use index::btree::LevelTree;
use index::btree::NodeCellRef;
use index::btree::{external, BPlusTree};
use index::lsmtree::tree::{LEVEL_2, LEVEL_3, LEVEL_PAGE_DIFF_MULTIPLIER};
use index::EntryKey;
use index::Slice;
use itertools::Itertools;
use smallvec::SmallVec;
use std::cmp::min;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt::Debug;
use std::mem;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::Arc;

enum Selection<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    Selected(Vec<NodeWriteGuard<KS, PS>>),
    Innode(NodeCellRef),
}

fn select<KS, PS>(node: &NodeCellRef) -> Vec<NodeWriteGuard<KS, PS>>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let search = mut_search::<KS, PS>(node, &smallvec!());
    match search {
        MutSearchResult::External => {
            let first_node = write_node(node);
            let mut collected_keys = first_node.len();
            let mut collected = vec![first_node];
            let target_keys = min(KS::slice_len() * LEVEL_PAGE_DIFF_MULTIPLIER, LEVEL_3);
            let target_guards = LEVEL_2;
            while collected_keys < target_keys && collected.len() < target_guards {
                let right = write_node(
                    collected
                        .last_mut()
                        .unwrap()
                        .right_ref_mut_no_empty()
                        .unwrap(),
                );
                if right.is_none() {
                    break;
                } else {
                    collected_keys += right.len();
                    collected.push(right);
                }
            }
            return collected;
        }
        MutSearchResult::Internal(node) => select::<KS, PS>(&node),
    }
}

fn prune_selected<'a, KS, PS>(node: &NodeCellRef, bound: &EntryKey) -> Vec<NodeCellRef>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let first_search = mut_search::<KS, PS>(node, &smallvec!());
    match first_search {
        MutSearchResult::Internal(sub_node_ref) => {
            let sub_node = read_unchecked::<KS, PS>(&sub_node_ref);
            if !sub_node.is_empty_node() && !sub_node.is_ext() {
                prune_selected::<KS, PS>(&sub_node_ref, bound);
            }
        }
        MutSearchResult::External => unreachable!(),
    };
    let mut prev_node_guard: Option<NodeWriteGuard<KS, PS>> = None;
    let mut page = write_node::<KS, PS>(&node);
    let mut next_live_ptrs = None;
    let mut emptying_nodes = vec![];
    let mut empty_nodes = vec![];
    let select_live = |page: &NodeWriteGuard<KS, PS>| {
        page.innode().ptrs.as_slice_immute()[..page.len() + 1]
            .iter()
            .enumerate()
            .filter_map(|(i, sub_level)| {
                if !read_unchecked::<KS, PS>(sub_level).is_empty_node() {
                    Some((i, sub_level.clone()))
                } else {
                    None
                }
            })
            .collect()
    };
    let make_empty = |page: &mut NodeWriteGuard<KS, PS>, right: &NodeCellRef| {
        **page = NodeData::Empty(box EmptyNode {
            left: None,
            right: right.clone(),
        });
    };
    let mut next_non_empty = |page: &NodeWriteGuard<KS, PS>, empty_nodes: &mut Vec<_>| {
        let mut next = page.right_ref().cloned();
        while let Some(next_ref) = next {
            let guard = write_node::<KS, PS>(&next_ref);
            next = guard.right_ref().cloned();
            let live_ptrs: HashMap<_, _> = select_live(&guard);
            if live_ptrs.is_empty() {
                empty_nodes.push(guard);
            } else {
                return (guard, live_ptrs);
            }
        };
        unreachable!()
    };
    loop {
        let page_len = page.len();
        let page_right_ref = page.right_ref().unwrap().clone();
        let page_right_bound = page.right_bound().clone();
        let next;
        if !page.is_empty_node() {
            let mut live_ptrs: HashMap<_, _> = next_live_ptrs.unwrap_or_else(|| select_live(&page));
            // debug_assert_ne!(live_ptrs.len(), 1);
            if live_ptrs.len() == 0 {
                // all sub nodes are empty
                // will set current node empty either
                next = next_non_empty(&page, &mut empty_nodes);
                empty_nodes.push(page);
            } else {
                let emptying = {
                    let mut innode = page.innode_mut();
                    let mut new_keys = KS::init();
                    let mut new_ptrs = PS::init();
                    {
                        let keys: Vec<&mut _> = innode.keys.as_slice()[..page_len]
                            .iter_mut()
                            .enumerate()
                            .filter(|(i, _)| live_ptrs.contains_key(&i))
                            .map(|(_, k)| k)
                            .collect();
                        debug!("Prune filtered page have keys {:?}", &keys);
                        debug_assert_eq!(live_ptrs.len(), keys.len() + 1);
                        innode.len = keys.len();
                        let new_keys = new_keys.as_slice();
                        let new_ptrs = new_ptrs.as_slice();
                        for (i, key) in keys.into_iter().enumerate() {
                            debug_assert!(key > &mut smallvec!(0));
                            mem::swap(&mut new_keys[i], key);
                        }
                        for (i, (_, ptr)) in live_ptrs.into_iter().enumerate() {
                            debug_assert!(!ptr.is_default());
                            mem::replace(&mut new_ptrs[i], ptr);
                        }
                    }
                    innode.keys = new_keys;
                    innode.ptrs = new_ptrs;
                    innode.len == 0
                };
                if emptying {
                    // current innode have one ptr and no keys
                    // need to merge with the right page
                    // if the right page is full, partial of the right page will be moved to the current page
                    // merging right page will also been cleaned

                }
                next = next_non_empty(&page, &mut empty_nodes);
                prev_node_guard = Some(page);
            }
            let (next_page, ptrs) = next;
            page = next_page;
            next_live_ptrs = Some(ptrs);
            if &page_right_bound >= bound {
                break;
            }
        } else {
            unreachable!();
        }
    }
    for empty in &mut empty_nodes {
        // make nodes empty and set its next ptr to last non empty node
        make_empty(empty, page.node_ref());
    }
    emptying_nodes
}

pub fn level_merge<KS, PS>(src_tree: &BPlusTree<KS, PS>, dest_tree: &LevelTree) -> usize
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut left_most_leaf_guards = select::<KS, PS>(&src_tree.get_root());
    let merge_page_len = left_most_leaf_guards.len();
    let mut num_keys_removed = 0;
    let left_most_id = left_most_leaf_guards.first().unwrap().ext_id();
    let prune_bound = left_most_leaf_guards.last().unwrap().right_bound().clone();

    debug!("Merge selected {} pages", left_most_leaf_guards.len());
    if cfg!(debug_assertions) {
        if left_most_id != src_tree.head_page_id {
            dump_tree(src_tree, "level_lsm_merge_failure_dump.json");
        }
    }
    debug_assert_eq!(
        left_most_id,
        src_tree.head_page_id,
        "{}",
        left_most_leaf_guards.first().unwrap().type_name()
    );

    // merge to dest_tree
    {
        let mut deleted_keys = src_tree.deleted.write();
        let mut merged_deleted_keys = vec![];
        let keys: Vec<EntryKey> = left_most_leaf_guards
            .iter()
            .filter(|&g| !g.is_empty_node())
            .map(|g| &g.keys()[..g.len()])
            .flatten()
            .filter(|&k| {
                if deleted_keys.contains(k) {
                    merged_deleted_keys.push(k.clone());
                    false
                } else {
                    true
                }
            })
            .cloned()
            .collect_vec();
        num_keys_removed = keys.len();
        debug!("Merge selected keys are {:?}", &keys);
        dest_tree.merge_with_keys(box keys);
        for rk in &merged_deleted_keys {
            deleted_keys.remove(rk);
        }
    }

    // adjust leaf left, right references
    {
        let right_right_most = left_most_leaf_guards
            .last()
            .unwrap()
            .right_ref()
            .unwrap()
            .clone();

        let left_left_most = left_most_leaf_guards
            .first()
            .unwrap()
            .left_ref()
            .unwrap()
            .clone();

        debug_assert!(read_unchecked::<KS, PS>(&left_left_most).is_none());
        debug_assert!(read_unchecked::<KS, PS>(&right_right_most).is_ext());
        debug_assert!(!Arc::ptr_eq(
            &right_right_most.inner,
            &left_most_leaf_guards.last().unwrap().node_ref().inner
        ));

        for mut g in &mut left_most_leaf_guards {
            external::make_deleted(&g.ext_id());
            **g = NodeData::Empty(box EmptyNode {
                left: Some(left_left_most.clone()),
                right: right_right_most.clone(),
            });
        }

        let mut new_first_node = write_node::<KS, PS>(&right_right_most);
        let mut new_first_node_ext = new_first_node.extnode_mut();
        new_first_node_ext.id = src_tree.head_page_id;
        new_first_node_ext.prev = left_left_most;

        debug_assert!(&new_first_node_ext.right_bound > &prune_bound);

        ExtNode::<KS, PS>::make_changed(&right_right_most, src_tree);
    }

    // cleanup upper level references
    prune_selected::<KS, PS>(&src_tree.get_root(), &prune_bound);

    src_tree.len.fetch_sub(num_keys_removed, Relaxed);

    merge_page_len
}
<commit_msg>handle empty node better<commit_after>use index::btree::dump::dump_tree;
use index::btree::external::ExtNode;
use index::btree::internal::InNode;
use index::btree::node::is_node_locked;
use index::btree::node::read_node;
use index::btree::node::read_unchecked;
use index::btree::node::write_node;
use index::btree::node::write_non_empty;
use index::btree::node::write_targeted;
use index::btree::node::EmptyNode;
use index::btree::node::Node;
use index::btree::node::NodeData;
use index::btree::node::NodeWriteGuard;
use index::btree::search::mut_search;
use index::btree::search::MutSearchResult;
use index::btree::LevelTree;
use index::btree::NodeCellRef;
use index::btree::{external, BPlusTree};
use index::lsmtree::tree::{LEVEL_2, LEVEL_3, LEVEL_PAGE_DIFF_MULTIPLIER};
use index::EntryKey;
use index::Slice;
use itertools::Itertools;
use smallvec::SmallVec;
use std::cmp::min;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt::Debug;
use std::mem;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::Arc;

enum Selection<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    Selected(Vec<NodeWriteGuard<KS, PS>>),
    Innode(NodeCellRef),
}

fn select<KS, PS>(node: &NodeCellRef) -> Vec<NodeWriteGuard<KS, PS>>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let search = mut_search::<KS, PS>(node, &smallvec!());
    match search {
        MutSearchResult::External => {
            let first_node = write_node(node);
            let mut collected_keys = first_node.len();
            let mut collected = vec![first_node];
            let target_keys = min(KS::slice_len() * LEVEL_PAGE_DIFF_MULTIPLIER, LEVEL_3);
            let target_guards = LEVEL_2;
            while collected_keys < target_keys && collected.len() < target_guards {
                let right = write_node(
                    collected
                        .last_mut()
                        .unwrap()
                        .right_ref_mut_no_empty()
                        .unwrap(),
                );
                if right.is_none() {
                    break;
                } else {
                    collected_keys += right.len();
                    collected.push(right);
                }
            }
            return collected;
        }
        MutSearchResult::Internal(node) => select::<KS, PS>(&node),
    }
}

fn prune_selected<'a, KS, PS>(node: &NodeCellRef, bound: &EntryKey) -> Vec<NodeCellRef>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let first_search = mut_search::<KS, PS>(node, &smallvec!());
    match first_search {
        MutSearchResult::Internal(sub_node_ref) => {
            let sub_node = read_unchecked::<KS, PS>(&sub_node_ref);
            if !sub_node.is_empty_node() && !sub_node.is_ext() {
                prune_selected::<KS, PS>(&sub_node_ref, bound);
            }
        }
        MutSearchResult::External => unreachable!(),
    };
    let mut prev_node_guard: Option<NodeWriteGuard<KS, PS>> = None;
    let mut page = write_node::<KS, PS>(&node);
    let mut next_live_ptrs = None;
    let mut emptying_nodes = vec![];
    let mut empty_nodes = vec![];
    let select_live = |page: &NodeWriteGuard<KS, PS>| {
        page.innode().ptrs.as_slice_immute()[..page.len() + 1]
            .iter()
            .enumerate()
            .filter_map(|(i, sub_level)| {
                if !read_unchecked::<KS, PS>(sub_level).is_empty_node() {
                    Some((i, sub_level.clone()))
                } else {
                    None
                }
            })
            .collect()
    };
    let mut next_non_empty = |page: &NodeWriteGuard<KS, PS>, empty_nodes: &mut Vec<_>| {
        let mut next = page.right_ref().cloned();
        while let Some(next_ref) = next {
            let guard = write_node::<KS, PS>(&next_ref);
            next = guard.right_ref().cloned();
            let live_ptrs: HashMap<_, _> = select_live(&guard);
            if live_ptrs.is_empty() {
                empty_nodes.push(guard);
            } else {
                return (guard, live_ptrs);
            }
        };
        unreachable!()
    };
    loop {
        let page_len = page.len();
        let page_right_ref = page.right_ref().unwrap().clone();
        if !page.is_empty_node() {
            let mut live_ptrs: HashMap<_, _> = next_live_ptrs.unwrap_or_else(|| select_live(&page));
            let is_empty = live_ptrs.is_empty();
            if !is_empty {
                let emptying = {
                    let mut innode = page.innode_mut();
                    let mut new_keys = KS::init();
                    let mut new_ptrs = PS::init();
                    {
                        let keys: Vec<&mut _> = innode.keys.as_slice()[..page_len]
                            .iter_mut()
                            .enumerate()
                            .filter(|(i, _)| live_ptrs.contains_key(&i))
                            .map(|(_, k)| k)
                            .collect();
                        debug!("Prune filtered page have keys {:?}", &keys);
                        debug_assert_eq!(live_ptrs.len(), keys.len() + 1);
                        innode.len = keys.len();
                        let new_keys = new_keys.as_slice();
                        let new_ptrs = new_ptrs.as_slice();
                        for (i, key) in keys.into_iter().enumerate() {
                            debug_assert!(key > &mut smallvec!(0));
                            mem::swap(&mut new_keys[i], key);
                        }
                        for (i, (_, ptr)) in live_ptrs.into_iter().enumerate() {
                            debug_assert!(!ptr.is_default());
                            mem::replace(&mut new_ptrs[i], ptr);
                        }
                    }
                    innode.keys = new_keys;
                    innode.ptrs = new_ptrs;
                    innode.len == 0
                };
                if emptying {
                    // current innode have one ptr and no keys
                    // need to merge with the right page
                    // if the right page is full, partial of the right page will be moved to the current page
                    // merging right page will also been cleaned

                }
            }
            let (next_page, ptrs) = next_non_empty(&page, &mut empty_nodes);
            if !is_empty {
                if page.right_bound() >= bound {
                    break;
                } else {
                    prev_node_guard = Some(page);
                }
            } else {
                empty_nodes.push(page);
            }
            page = next_page;
            next_live_ptrs = Some(ptrs);
        } else {
            unreachable!();
        }
    }
    let last_page_ref = page.node_ref();
    for empty in &mut empty_nodes {
        // make nodes empty and set its next ptr to last non empty node
        **empty = NodeData::Empty(box EmptyNode {
            left: None,
            right: last_page_ref.clone(),
        });
    }
    emptying_nodes
}

pub fn level_merge<KS, PS>(src_tree: &BPlusTree<KS, PS>, dest_tree: &LevelTree) -> usize
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut left_most_leaf_guards = select::<KS, PS>(&src_tree.get_root());
    let merge_page_len = left_most_leaf_guards.len();
    let mut num_keys_removed = 0;
    let left_most_id = left_most_leaf_guards.first().unwrap().ext_id();
    let prune_bound = left_most_leaf_guards.last().unwrap().right_bound().clone();

    debug!("Merge selected {} pages", left_most_leaf_guards.len());
    if cfg!(debug_assertions) {
        if left_most_id != src_tree.head_page_id {
            dump_tree(src_tree, "level_lsm_merge_failure_dump.json");
        }
    }
    debug_assert_eq!(
        left_most_id,
        src_tree.head_page_id,
        "{}",
        left_most_leaf_guards.first().unwrap().type_name()
    );

    // merge to dest_tree
    {
        let mut deleted_keys = src_tree.deleted.write();
        let mut merged_deleted_keys = vec![];
        let keys: Vec<EntryKey> = left_most_leaf_guards
            .iter()
            .filter(|&g| !g.is_empty_node())
            .map(|g| &g.keys()[..g.len()])
            .flatten()
            .filter(|&k| {
                if deleted_keys.contains(k) {
                    merged_deleted_keys.push(k.clone());
                    false
                } else {
                    true
                }
            })
            .cloned()
            .collect_vec();
        num_keys_removed = keys.len();
        debug!("Merge selected keys are {:?}", &keys);
        dest_tree.merge_with_keys(box keys);
        for rk in &merged_deleted_keys {
            deleted_keys.remove(rk);
        }
    }

    // adjust leaf left, right references
    {
        let right_right_most = left_most_leaf_guards
            .last()
            .unwrap()
            .right_ref()
            .unwrap()
            .clone();

        let left_left_most = left_most_leaf_guards
            .first()
            .unwrap()
            .left_ref()
            .unwrap()
            .clone();

        debug_assert!(read_unchecked::<KS, PS>(&left_left_most).is_none());
        debug_assert!(read_unchecked::<KS, PS>(&right_right_most).is_ext());
        debug_assert!(!Arc::ptr_eq(
            &right_right_most.inner,
            &left_most_leaf_guards.last().unwrap().node_ref().inner
        ));

        for mut g in &mut left_most_leaf_guards {
            external::make_deleted(&g.ext_id());
            **g = NodeData::Empty(box EmptyNode {
                left: Some(left_left_most.clone()),
                right: right_right_most.clone(),
            });
        }

        let mut new_first_node = write_node::<KS, PS>(&right_right_most);
        let mut new_first_node_ext = new_first_node.extnode_mut();
        new_first_node_ext.id = src_tree.head_page_id;
        new_first_node_ext.prev = left_left_most;

        debug_assert!(&new_first_node_ext.right_bound > &prune_bound);

        ExtNode::<KS, PS>::make_changed(&right_right_most, src_tree);
    }

    // cleanup upper level references
    prune_selected::<KS, PS>(&src_tree.get_root(), &prune_bound);

    src_tree.len.fetch_sub(num_keys_removed, Relaxed);

    merge_page_len
}
<|endoftext|>


<commit_before>use index::btree::external::ExtNode;
use index::btree::insert::check_root_modification;
use index::btree::internal::InNode;
use index::btree::node::read_node;
use index::btree::node::read_unchecked;
use index::btree::node::write_node;
use index::btree::node::write_non_empty;
use index::btree::node::write_targeted;
use index::btree::node::Node;
use index::btree::node::NodeData;
use index::btree::node::NodeReadHandler;
use index::btree::search::mut_search;
use index::btree::search::MutSearchResult;
use index::btree::BPlusTree;
use index::btree::NodeCellRef;
use index::EntryKey;
use index::Slice;
use itertools::Itertools;
use std::fmt::Debug;

enum MergeSearch {
    External,
    Internal(NodeCellRef),
    RightNode(NodeCellRef),
}

fn merge_into_internal<KS, PS>(
    node: &NodeCellRef,
    lower_level_new_pages: Box<Vec<(EntryKey, NodeCellRef)>>,
    new_pages: &mut Vec<(EntryKey, NodeCellRef)>,
) where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut node_guard = write_node::<KS, PS>(node);
    for (pivot, node) in lower_level_new_pages.into_iter() {
        let mut target_guard = write_non_empty(write_targeted(node_guard, &pivot));
        {
            debug_assert!(!target_guard.is_none());
            let innode = target_guard.innode_mut();
            let pos = innode.search(&pivot);
            if innode.len == KS::slice_len() - 1 {
                // TODO: check boundary
                // full node, going to split
                let (node_ref, key) = innode.split_insert(pivot, node, pos, true);
                new_pages.push((key, node_ref));
            } else {
                innode.insert_in_place(pivot, node, pos, true);
            }
        }
        node_guard = target_guard;
    }
}

fn debug_check_serialized(keys: &Vec<EntryKey>) {
    if cfg!(debug_assertions) && keys.len() > 0 {
        for i in 0..keys.len() - 1 {
            assert!(keys[i] < keys[i + 1])
        }
    }
}

pub fn merge_into_tree_node<KS, PS>(
    tree: &BPlusTree<KS, PS>,
    node: &NodeCellRef,
    _parent: &NodeCellRef,
    keys: Box<Vec<EntryKey>>,
    level: usize,
) -> Box<Vec<(EntryKey, NodeCellRef)>>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug_check_serialized(&*keys);
    let search = mut_search::<KS, PS>(node, &keys[0]);
    let new_pages = match search {
        MutSearchResult::External => {
            // merge keys into internal pages
            // this is a oneshot action.
            // after the merge, it will return all new inserted new pages to upper level
            debug!("Merge into internal with keys {:?}", &keys);
            let keys_len = keys.len();
            let mut merging_pos = 0;
            let mut current_guard = write_node::<KS, PS>(&node);
            let mut new_pages = vec![];
            // merge by pages
            while merging_pos < keys_len {
                let start_key = &keys[merging_pos];
                debug!("Start merging with page at {:?}", start_key);
                current_guard = write_targeted(current_guard, start_key);
                let remain_slots = KS::slice_len() - current_guard.len();
                ExtNode::<KS, PS>::make_changed(current_guard.node_ref(), tree);
                if remain_slots > 0 {
                    let mut ext_node = current_guard.extnode_mut();
                    ext_node.remove_contains(&mut *tree.deleted.write());
                    let selection = keys[merging_pos..keys_len]
                        .iter()
                        .filter(|&k| k < &ext_node.right_bound)
                        .take(remain_slots)
                        .collect_vec();
                    ext_node.merge_sort(selection.as_slice());
                    merging_pos += selection.len();
                } else if remain_slots == 0 {
                    let insert_pos = current_guard.search(&start_key);
                    let target_node_ref = current_guard.node_ref().clone();
                    let mut right_guard =
                        write_node::<KS, PS>(current_guard.right_ref_mut_no_empty().unwrap());
                    let (new_node, pivot) = current_guard.extnode_mut().split_insert(
                        start_key.clone(),
                        insert_pos,
                        &target_node_ref,
                        &mut right_guard,
                        tree,
                    );
                    merging_pos += 1;
                    ExtNode::<KS, PS>::make_changed(&new_node, tree);
                    new_pages.push((pivot, new_node));
                }
            }
            new_pages
        }
        MutSearchResult::Internal(sub_node) => {
            debug_assert!(!sub_node.is_default());
            let lower_level_new_pages =
                merge_into_tree_node(tree, &sub_node, node, keys, level + 1);
            let mut new_pages = vec![];
            if lower_level_new_pages.len() > 0 {
                merge_into_internal::<KS, PS>(node, lower_level_new_pages, &mut new_pages);
            }
            new_pages
        }
    };
    if level == 0 && new_pages.len() > 0 {
        // it is impossible to have a node been changed during merge for merges are performed in serial
        debug_assert_eq!(
            read_unchecked::<KS, PS>(&tree.get_root()).first_key(),
            read_unchecked::<KS, PS>(&node).first_key()
        )
    }
    return box new_pages;
}
<commit_msg>highlight ordering check<commit_after>use index::btree::external::ExtNode;
use index::btree::insert::check_root_modification;
use index::btree::internal::InNode;
use index::btree::node::read_node;
use index::btree::node::read_unchecked;
use index::btree::node::write_node;
use index::btree::node::write_non_empty;
use index::btree::node::write_targeted;
use index::btree::node::Node;
use index::btree::node::NodeData;
use index::btree::node::NodeReadHandler;
use index::btree::search::mut_search;
use index::btree::search::MutSearchResult;
use index::btree::BPlusTree;
use index::btree::NodeCellRef;
use index::EntryKey;
use index::Slice;
use itertools::Itertools;
use std::fmt::Debug;

enum MergeSearch {
    External,
    Internal(NodeCellRef),
    RightNode(NodeCellRef),
}

fn merge_into_internal<KS, PS>(
    node: &NodeCellRef,
    lower_level_new_pages: Box<Vec<(EntryKey, NodeCellRef)>>,
    new_pages: &mut Vec<(EntryKey, NodeCellRef)>,
) where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut node_guard = write_node::<KS, PS>(node);
    for (pivot, node) in lower_level_new_pages.into_iter() {
        let mut target_guard = write_non_empty(write_targeted(node_guard, &pivot));
        {
            debug_assert!(!target_guard.is_none());
            let innode = target_guard.innode_mut();
            let pos = innode.search(&pivot);
            if innode.len == KS::slice_len() - 1 {
                // TODO: check boundary
                // full node, going to split
                let (node_ref, key) = innode.split_insert(pivot, node, pos, true);
                new_pages.push((key, node_ref));
            } else {
                innode.insert_in_place(pivot, node, pos, true);
            }
        }
        node_guard = target_guard;
    }
}

fn debug_check_serialized(keys: &Vec<EntryKey>) {
    if cfg!(debug_assertions) && keys.len() > 0 {
        for i in 0..keys.len() - 1 {
            let left = &keys[i];
            let right = &keys[i + 1];
            assert!(left < right, "at {}, {:?} >= {:?}", i, left, right);
        }
    }
}

pub fn merge_into_tree_node<KS, PS>(
    tree: &BPlusTree<KS, PS>,
    node: &NodeCellRef,
    _parent: &NodeCellRef,
    keys: Box<Vec<EntryKey>>,
    level: usize,
) -> Box<Vec<(EntryKey, NodeCellRef)>>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug_check_serialized(&*keys);
    let search = mut_search::<KS, PS>(node, &keys[0]);
    let new_pages = match search {
        MutSearchResult::External => {
            // merge keys into internal pages
            // this is a oneshot action.
            // after the merge, it will return all new inserted new pages to upper level
            debug!("Merge into internal with keys {:?}", &keys);
            let keys_len = keys.len();
            let mut merging_pos = 0;
            let mut current_guard = write_node::<KS, PS>(&node);
            let mut new_pages = vec![];
            // merge by pages
            while merging_pos < keys_len {
                let start_key = &keys[merging_pos];
                debug!("Start merging with page at {:?}", start_key);
                current_guard = write_targeted(current_guard, start_key);
                let remain_slots = KS::slice_len() - current_guard.len();
                ExtNode::<KS, PS>::make_changed(current_guard.node_ref(), tree);
                if remain_slots > 0 {
                    let mut ext_node = current_guard.extnode_mut();
                    ext_node.remove_contains(&mut *tree.deleted.write());
                    let selection = keys[merging_pos..keys_len]
                        .iter()
                        .filter(|&k| k < &ext_node.right_bound)
                        .take(remain_slots)
                        .collect_vec();
                    ext_node.merge_sort(selection.as_slice());
                    merging_pos += selection.len();
                } else if remain_slots == 0 {
                    let insert_pos = current_guard.search(&start_key);
                    let target_node_ref = current_guard.node_ref().clone();
                    let mut right_guard =
                        write_node::<KS, PS>(current_guard.right_ref_mut_no_empty().unwrap());
                    let (new_node, pivot) = current_guard.extnode_mut().split_insert(
                        start_key.clone(),
                        insert_pos,
                        &target_node_ref,
                        &mut right_guard,
                        tree,
                    );
                    merging_pos += 1;
                    ExtNode::<KS, PS>::make_changed(&new_node, tree);
                    new_pages.push((pivot, new_node));
                }
            }
            new_pages
        }
        MutSearchResult::Internal(sub_node) => {
            debug_assert!(!sub_node.is_default());
            let lower_level_new_pages =
                merge_into_tree_node(tree, &sub_node, node, keys, level + 1);
            let mut new_pages = vec![];
            if lower_level_new_pages.len() > 0 {
                merge_into_internal::<KS, PS>(node, lower_level_new_pages, &mut new_pages);
            }
            new_pages
        }
    };
    if level == 0 && new_pages.len() > 0 {
        // it is impossible to have a node been changed during merge for merges are performed in serial
        debug_assert_eq!(
            read_unchecked::<KS, PS>(&tree.get_root()).first_key(),
            read_unchecked::<KS, PS>(&node).first_key()
        )
    }
    return box new_pages;
}
<|endoftext|>