<commit_before>extern crate half;
extern crate openexr;
extern crate openexr_sys;

use half::f16;
use openexr::{FrameBuffer, FrameBufferMut, Header, InputFile, ScanlineOutputFile};
use std::fs::File;

// OpenEXR file data.
const NEGATIVE_OFFSET: &[u8] = include_bytes!("data/negative_window.exr");
const POSITIVE_OFFSET: &[u8] = include_bytes!("data/positive_window.exr");

fn load_and_test_with_offset_window_read_multiple_channels(data: &[u8]) {
    let mut exr_file = InputFile::from_slice(data).unwrap();

    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    println!("Reading pixels from {},{},{}", "R", "G", "B");
    let mut pixel_data = vec![(zero, zero, zero); (width * height) as usize];

    // let's try a mismatched origin 0,0
    let read_with_offset = |exr_file: &mut InputFile, pixel_data: &mut [_], ox, oy| {
        let mut fb = FrameBufferMut::new_with_origin(ox, oy, width, height);
        println!("Loading from buffer as {},{} {}x{}", ox, oy, width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], pixel_data);
        exr_file.read_pixels(&mut fb)
    };

    assert!(read_with_offset(&mut exr_file, &mut pixel_data, 0, 0).is_err());
    assert!(read_with_offset(&mut exr_file, &mut pixel_data, x - 1, y - 1).is_err());
    assert!(read_with_offset(&mut exr_file, &mut pixel_data, -x, -y).is_err());
    assert!(read_with_offset(&mut exr_file, &mut pixel_data, x, y).is_ok());

    // and then the real thing
    let origin_offset = {
        let mut fb = FrameBufferMut::new_with_origin(x, y, width, height);
        println!("Loading from buffer as {}x{}", width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], &mut pixel_data);
        exr_file.read_pixels(&mut fb).unwrap();
        fb.origin_offset()
    };

    // check the pixel value at coordinates (0,0) of the data window if 0,0 is within the frame buffer
    if origin_offset >= 0 {
        assert!(f32::abs(pixel_data[origin_offset as usize].0.to_f32() - 0.5f32) < 0.0001f32);
    }

    // we write the file back out with a different offset
    {
        let mut fb = FrameBuffer::new_with_origin(-x, -y, width, height);
        println!("Loading buffer as {}x{}", width, height);
        let mut file =
            File::create("target/positive_window.exr").expect("Could not create output file");
        let mut exr_file = ScanlineOutputFile::new(
            &mut file,
            &Header::new()
                .set_data_window(Header::box2i(-x, -y, width, height))
                .set_display_window(Header::box2i(0, 0, width - 16, height - 16))
                .add_channel("R", openexr::PixelType::HALF)
                .add_channel("G", openexr::PixelType::HALF)
                .add_channel("B", openexr::PixelType::HALF),
        )
        .unwrap();

        fb.insert_channels(&["R", "G", "B"], &pixel_data);
        exr_file.write_pixels(&fb).unwrap();
    }
}

fn load_and_test_with_offset_window_read_single_channel(data: &[u8]) {
    let mut exr_file = InputFile::from_slice(data).unwrap();
    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    println!("Reading pixels from {},{},{}", "R", "G", "B");
    let mut pixel_data = vec![zero; (width * height) as usize];

    // let's try a mismatched origin near the origin
    let mut read_with_offset = |ox: i32, oy: i32| {
        let mut fb = FrameBufferMut::new_with_origin(ox, oy, width, height);
        println!(
            "Loading from buffer as ({} {}) {}x{}",
            ox, oy, width, height
        );
        fb.insert_channel("R", 0.0, &mut pixel_data);
        exr_file.read_pixels(&mut fb)
    };

    assert!(read_with_offset(0, 0).is_err());
    assert!(read_with_offset(x - 1, y - 1).is_err());
    assert!(read_with_offset(-x, -y).is_err());
    assert!(read_with_offset(x, y).is_ok());
}

#[test]
fn window_read_multiple_channels() {
    load_and_test_with_offset_window_read_multiple_channels(NEGATIVE_OFFSET);
    load_and_test_with_offset_window_read_multiple_channels(POSITIVE_OFFSET);
}

// with one channel only as well
#[test]
fn negative_window_read_single_channel() {
    load_and_test_with_offset_window_read_single_channel(NEGATIVE_OFFSET);
    load_and_test_with_offset_window_read_single_channel(POSITIVE_OFFSET);
}
<commit_msg>Better comments in the new example<commit_after>extern crate half;
extern crate openexr;
extern crate openexr_sys;

use half::f16;
use openexr::{FrameBuffer, FrameBufferMut, Header, InputFile, ScanlineOutputFile};
use std::fs::File;

// OpenEXR file data.
const NEGATIVE_OFFSET: &[u8] = include_bytes!("data/negative_window.exr");
const POSITIVE_OFFSET: &[u8] = include_bytes!("data/positive_window.exr");

fn load_and_test_with_offset_window_read_multiple_channels(data: &[u8]) {
    let mut exr_file = InputFile::from_slice(data).unwrap();

    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    let mut pixel_data = vec![(zero, zero, zero); (width * height) as usize];

    let read_with_offset = |exr_file: &mut InputFile, pixel_data: &mut [_], ox, oy| {
        let mut fb = FrameBufferMut::new_with_origin(ox, oy, width, height);
        println!("Loading from buffer as {},{} {}x{}", ox, oy, width, height);
        fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], pixel_data);
        println!("Reading pixels from {},{},{}", "R", "G", "B");
        (exr_file.read_pixels(&mut fb), fb.origin_offset())
    };

    // let's try a few mismatched origins
    assert!(read_with_offset(&mut exr_file, &mut pixel_data, 0, 0)
        .0
        .is_err());
    assert!(
        read_with_offset(&mut exr_file, &mut pixel_data, x - 1, y - 1)
            .0
            .is_err()
    );
    assert!(read_with_offset(&mut exr_file, &mut pixel_data, -x, -y)
        .0
        .is_err());
    // and then the real thing
    let (read_result, origin_offset) = read_with_offset(&mut exr_file, &mut pixel_data, x, y);
    assert!(read_result.is_ok());
    // check the pixel value at coordinates (0,0) of the data window if 0,0 is within the frame buffer
    if origin_offset >= 0 {
        assert!(f32::abs(pixel_data[origin_offset as usize].0.to_f32() - 0.5f32) < 0.0001f32);
    }

    // we write the file back out with a different offset
    {
        let mut fb = FrameBuffer::new_with_origin(-x, -y, width, height);
        println!("Loading buffer as {}x{}", width, height);
        let mut file =
            File::create("target/positive_window.exr").expect("Could not create output file");
        let mut exr_file = ScanlineOutputFile::new(
            &mut file,
            &Header::new()
                .set_data_window(Header::box2i(-x, -y, width, height))
                .set_display_window(Header::box2i(0, 0, width - 16, height - 16))
                .add_channel("R", openexr::PixelType::HALF)
                .add_channel("G", openexr::PixelType::HALF)
                .add_channel("B", openexr::PixelType::HALF),
        )
        .unwrap();

        fb.insert_channels(&["R", "G", "B"], &pixel_data);
        exr_file.write_pixels(&fb).unwrap();
    }
}

fn load_and_test_with_offset_window_read_single_channel(data: &[u8]) {
    let mut exr_file = InputFile::from_slice(data).unwrap();
    let (width, height) = exr_file.header().data_dimensions();
    let (x, y) = exr_file.header().data_origin();

    let zero = f16::from_f32(0.0f32);

    let mut pixel_data = vec![zero; (width * height) as usize];

    let mut read_with_offset = |ox: i32, oy: i32| {
        let mut fb = FrameBufferMut::new_with_origin(ox, oy, width, height);
        println!(
            "Loading from buffer as ({} {}) {}x{}",
            ox, oy, width, height
        );
        fb.insert_channel("R", 0.0, &mut pixel_data);
        println!("Reading pixels from {},{},{}", "R", "G", "B");
        exr_file.read_pixels(&mut fb)
    };
    // let's try a few mismatched origins
    assert!(read_with_offset(0, 0).is_err());
    assert!(read_with_offset(x - 1, y - 1).is_err());
    assert!(read_with_offset(-x, -y).is_err());
    // and then the correct one
    assert!(read_with_offset(x, y).is_ok());
}

#[test]
fn window_read_multiple_channels() {
    load_and_test_with_offset_window_read_multiple_channels(NEGATIVE_OFFSET);
    load_and_test_with_offset_window_read_multiple_channels(POSITIVE_OFFSET);
}

// with one channel only as well
#[test]
fn negative_window_read_single_channel() {
    load_and_test_with_offset_window_read_single_channel(NEGATIVE_OFFSET);
    load_and_test_with_offset_window_read_single_channel(POSITIVE_OFFSET);
}
<|endoftext|>


<commit_before>// xfail-stage0
// xfail-stage1
// xfail-stage2
// error-pattern:explicit failure
fn f(int a) {
  log a;
}

fn main() {
  f(fail);
}
<commit_msg>Un-XFAIL fail-arg<commit_after>// xfail-stage0
// error-pattern:woe
fn f(int a) {
  log a;
}

fn main() {
  f(fail "woe");
}
<|endoftext|>


<commit_before>// Copyright 2017 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 core::any::*;
use test::{Bencher, black_box};

#[bench]
fn bench_downcast_ref(b: &mut Bencher) {
    b.iter(|| {
        let mut x = 0;
        let mut y = &mut x as &mut Any;
        black_box(&mut y);
        black_box(y.downcast_ref::<isize>() == Some(&0));
    });
}
<commit_msg>Rollup merge of #53781 - matthiaskrgr:fix_any_bench, r=kennytm<commit_after>// Copyright 2017 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 core::any::*;
use test::{Bencher, black_box};

#[bench]
fn bench_downcast_ref(b: &mut Bencher) {
    b.iter(|| {
        let mut x = 0;
        let mut y = &mut x as &mut dyn Any;
        black_box(&mut y);
        black_box(y.downcast_ref::<isize>() == Some(&0));
    });
}
<|endoftext|>


<commit_before>//! Windows SEH
//!
//! On Windows (currently only on MSVC), the default exception handling
//! mechanism is Structured Exception Handling (SEH). This is quite different
//! than Dwarf-based exception handling (e.g., what other unix platforms use) in
//! terms of compiler internals, so LLVM is required to have a good deal of
//! extra support for SEH.
//!
//! In a nutshell, what happens here is:
//!
//! 1. The `panic` function calls the standard Windows function
//!    `_CxxThrowException` to throw a C++-like exception, triggering the
//!    unwinding process.
//! 2. All landing pads generated by the compiler use the personality function
//!    `__CxxFrameHandler3`, a function in the CRT, and the unwinding code in
//!    Windows will use this personality function to execute all cleanup code on
//!    the stack.
//! 3. All compiler-generated calls to `invoke` have a landing pad set as a
//!    `cleanuppad` LLVM instruction, which indicates the start of the cleanup
//!    routine. The personality (in step 2, defined in the CRT) is responsible
//!    for running the cleanup routines.
//! 4. Eventually the "catch" code in the `try` intrinsic (generated by the
//!    compiler) is executed and indicates that control should come back to
//!    Rust. This is done via a `catchswitch` plus a `catchpad` instruction in
//!    LLVM IR terms, finally returning normal control to the program with a
//!    `catchret` instruction.
//!
//! Some specific differences from the gcc-based exception handling are:
//!
//! * Rust has no custom personality function, it is instead *always*
//!   `__CxxFrameHandler3`. Additionally, no extra filtering is performed, so we
//!   end up catching any C++ exceptions that happen to look like the kind we're
//!   throwing. Note that throwing an exception into Rust is undefined behavior
//!   anyway, so this should be fine.
//! * We've got some data to transmit across the unwinding boundary,
//!   specifically a `Box<dyn Any + Send>`. Like with Dwarf exceptions
//!   these two pointers are stored as a payload in the exception itself. On
//!   MSVC, however, there's no need for an extra heap allocation because the
//!   call stack is preserved while filter functions are being executed. This
//!   means that the pointers are passed directly to `_CxxThrowException` which
//!   are then recovered in the filter function to be written to the stack frame
//!   of the `try` intrinsic.
//!
//! [win64]: https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64
//! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions

#![allow(nonstandard_style)]
#![allow(private_no_mangle_fns)]

use alloc::boxed::Box;
use core::any::Any;
use core::mem;
use core::raw;
use libc::{c_int, c_uint, c_void};

// First up, a whole bunch of type definitions. There's a few platform-specific
// oddities here, and a lot that's just blatantly copied from LLVM. The purpose
// of all this is to implement the `panic` function below through a call to
// `_CxxThrowException`.
//
// This function takes two arguments. The first is a pointer to the data we're
// passing in, which in this case is our trait object. Pretty easy to find! The
// next, however, is more complicated. This is a pointer to a `_ThrowInfo`
// structure, and it generally is just intended to just describe the exception
// being thrown.
//
// Currently the definition of this type [1] is a little hairy, and the main
// oddity (and difference from the online article) is that on 32-bit the
// pointers are pointers but on 64-bit the pointers are expressed as 32-bit
// offsets from the `__ImageBase` symbol. The `ptr_t` and `ptr!` macro in the
// modules below are used to express this.
//
// The maze of type definitions also closely follows what LLVM emits for this
// sort of operation. For example, if you compile this C++ code on MSVC and emit
// the LLVM IR:
//
//      #include <stdint.h>
//
//      struct rust_panic {
//          rust_panic(const rust_panic&);
//          ~rust_panic();
//
//          uint64_t x[2];
//      };
//
//      void foo() {
//          rust_panic a = {0, 1};
//          throw a;
//      }
//
// That's essentially what we're trying to emulate. Most of the constant values
// below were just copied from LLVM,
//
// In any case, these structures are all constructed in a similar manner, and
// it's just somewhat verbose for us.
//
// [1]: http://www.geoffchappell.com/studies/msvc/language/predefined/

#[cfg(target_arch = "x86")]
#[macro_use]
mod imp {
    pub type ptr_t = *mut u8;

    macro_rules! ptr {
        (0) => {
            core::ptr::null_mut()
        };
        ($e:expr) => {
            $e as *mut u8
        };
    }
}

#[cfg(any(target_arch = "x86_64", target_arch = "arm"))]
#[macro_use]
mod imp {
    pub type ptr_t = u32;

    extern "C" {
        pub static __ImageBase: u8;
    }

    macro_rules! ptr {
        (0) => (0);
        ($e:expr) => {
            (($e as usize) - (&imp::__ImageBase as *const _ as usize)) as u32
        }
    }
}

#[repr(C)]
pub struct _ThrowInfo {
    pub attributes: c_uint,
    pub pmfnUnwind: imp::ptr_t,
    pub pForwardCompat: imp::ptr_t,
    pub pCatchableTypeArray: imp::ptr_t,
}

#[repr(C)]
pub struct _CatchableTypeArray {
    pub nCatchableTypes: c_int,
    pub arrayOfCatchableTypes: [imp::ptr_t; 1],
}

#[repr(C)]
pub struct _CatchableType {
    pub properties: c_uint,
    pub pType: imp::ptr_t,
    pub thisDisplacement: _PMD,
    pub sizeOrOffset: c_int,
    pub copyFunction: imp::ptr_t,
}

#[repr(C)]
pub struct _PMD {
    pub mdisp: c_int,
    pub pdisp: c_int,
    pub vdisp: c_int,
}

#[repr(C)]
pub struct _TypeDescriptor {
    pub pVFTable: *const u8,
    pub spare: *mut u8,
    pub name: [u8; 11],
}

// Note that we intentionally ignore name mangling rules here: we don't want C++
// to be able to catch Rust panics by simply declaring a `struct rust_panic`.
const TYPE_NAME: [u8; 11] = *b"rust_panic\0";

static mut THROW_INFO: _ThrowInfo = _ThrowInfo {
    attributes: 0,
    pmfnUnwind: ptr!(0),
    pForwardCompat: ptr!(0),
    pCatchableTypeArray: ptr!(0),
};

static mut CATCHABLE_TYPE_ARRAY: _CatchableTypeArray =
    _CatchableTypeArray { nCatchableTypes: 1, arrayOfCatchableTypes: [ptr!(0)] };

static mut CATCHABLE_TYPE: _CatchableType = _CatchableType {
    properties: 0,
    pType: ptr!(0),
    thisDisplacement: _PMD { mdisp: 0, pdisp: -1, vdisp: 0 },
    sizeOrOffset: mem::size_of::<[u64; 2]>() as c_int,
    copyFunction: ptr!(0),
};

extern "C" {
    // The leading `\x01` byte here is actually a magical signal to LLVM to
    // *not* apply any other mangling like prefixing with a `_` character.
    //
    // This symbol is the vtable used by C++'s `std::type_info`. Objects of type
    // `std::type_info`, type descriptors, have a pointer to this table. Type
    // descriptors are referenced by the C++ EH structures defined above and
    // that we construct below.
    #[link_name = "\x01??_7type_info@@6B@"]
    static TYPE_INFO_VTABLE: *const u8;
}

// We use #[lang = "eh_catch_typeinfo"] here as this is the type descriptor which
// we'll use in LLVM's `catchpad` instruction which ends up also being passed as
// an argument to the C++ personality function.
//
// Again, I'm not entirely sure what this is describing, it just seems to work.
#[cfg_attr(not(test), lang = "eh_catch_typeinfo")]
static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
    pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _,
    spare: core::ptr::null_mut(),
    name: TYPE_NAME,
};

// Destructor used if the C++ code decides to capture the exception and drop it
// without propagating it. The catch part of the try intrinsic will set the
// first word of the exception object to 0 so that it is skipped by the
// destructor.
//
// Note that x86 Windows uses the "thiscall" calling convention for C++ member
// functions instead of the default "C" calling convention.
cfg_if::cfg_if! {
    if #[cfg(target_arch = "x86")] {
        unsafe extern "thiscall" fn exception_cleanup(e: *mut [u64; 2]) {
            if (*e)[0] != 0 {
                cleanup(*e);
            }
        }
        unsafe extern "thiscall" fn exception_copy(_dest: *mut [u64; 2],
                                                   _src: *mut [u64; 2])
                                                   -> *mut [u64; 2] {
            panic!("Rust panics cannot be copied");
        }
    } else {
        unsafe extern "C" fn exception_cleanup(e: *mut [u64; 2]) {
            if (*e)[0] != 0 {
                cleanup(*e);
            }
        }
        unsafe extern "C" fn exception_copy(_dest: *mut [u64; 2],
                                            _src: *mut [u64; 2])
                                            -> *mut [u64; 2] {
            panic!("Rust panics cannot be copied");
        }
    }
}

pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
    use core::intrinsics::atomic_store;

    // _CxxThrowException executes entirely on this stack frame, so there's no
    // need to otherwise transfer `data` to the heap. We just pass a stack
    // pointer to this function.
    //
    // The first argument is the payload being thrown (our two pointers), and
    // the second argument is the type information object describing the
    // exception (constructed above).
    let ptrs = mem::transmute::<_, raw::TraitObject>(data);
    let mut ptrs = [ptrs.data as u64, ptrs.vtable as u64];
    let throw_ptr = ptrs.as_mut_ptr() as *mut _;

    // This... may seems surprising, and justifiably so. On 32-bit MSVC the
    // pointers between these structure are just that, pointers. On 64-bit MSVC,
    // however, the pointers between structures are rather expressed as 32-bit
    // offsets from `__ImageBase`.
    //
    // Consequently, on 32-bit MSVC we can declare all these pointers in the
    // `static`s above. On 64-bit MSVC, we would have to express subtraction of
    // pointers in statics, which Rust does not currently allow, so we can't
    // actually do that.
    //
    // The next best thing, then is to fill in these structures at runtime
    // (panicking is already the "slow path" anyway). So here we reinterpret all
    // of these pointer fields as 32-bit integers and then store the
    // relevant value into it (atomically, as concurrent panics may be
    // happening). Technically the runtime will probably do a nonatomic read of
    // these fields, but in theory they never read the *wrong* value so it
    // shouldn't be too bad...
    //
    // In any case, we basically need to do something like this until we can
    // express more operations in statics (and we may never be able to).
    if !cfg!(bootstrap) {
        atomic_store(
            &mut THROW_INFO.pmfnUnwind as *mut _ as *mut u32,
            ptr!(exception_cleanup) as u32,
        );
    }
    atomic_store(
        &mut THROW_INFO.pCatchableTypeArray as *mut _ as *mut u32,
        ptr!(&CATCHABLE_TYPE_ARRAY as *const _) as u32,
    );
    atomic_store(
        &mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0] as *mut _ as *mut u32,
        ptr!(&CATCHABLE_TYPE as *const _) as u32,
    );
    atomic_store(
        &mut CATCHABLE_TYPE.pType as *mut _ as *mut u32,
        ptr!(&TYPE_DESCRIPTOR as *const _) as u32,
    );
    if !cfg!(bootstrap) {
        atomic_store(
            &mut CATCHABLE_TYPE.copyFunction as *mut _ as *mut u32,
            ptr!(exception_copy) as u32,
        );
    }

    extern "system" {
        #[unwind(allowed)]
        pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
    }

    _CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
}

pub fn payload() -> [u64; 2] {
    [0; 2]
}

pub unsafe fn cleanup(payload: [u64; 2]) -> Box<dyn Any + Send> {
    mem::transmute(raw::TraitObject { data: payload[0] as *mut _, vtable: payload[1] as *mut _ })
}

// This is required by the compiler to exist (e.g., it's a lang item), but
// it's never actually called by the compiler because __C_specific_handler
// or _except_handler3 is the personality function that is always used.
// Hence this is just an aborting stub.
#[lang = "eh_personality"]
#[cfg(not(test))]
fn rust_eh_personality() {
    unsafe { core::intrinsics::abort() }
}
<commit_msg>Explain the panic! in exception_copy<commit_after>//! Windows SEH
//!
//! On Windows (currently only on MSVC), the default exception handling
//! mechanism is Structured Exception Handling (SEH). This is quite different
//! than Dwarf-based exception handling (e.g., what other unix platforms use) in
//! terms of compiler internals, so LLVM is required to have a good deal of
//! extra support for SEH.
//!
//! In a nutshell, what happens here is:
//!
//! 1. The `panic` function calls the standard Windows function
//!    `_CxxThrowException` to throw a C++-like exception, triggering the
//!    unwinding process.
//! 2. All landing pads generated by the compiler use the personality function
//!    `__CxxFrameHandler3`, a function in the CRT, and the unwinding code in
//!    Windows will use this personality function to execute all cleanup code on
//!    the stack.
//! 3. All compiler-generated calls to `invoke` have a landing pad set as a
//!    `cleanuppad` LLVM instruction, which indicates the start of the cleanup
//!    routine. The personality (in step 2, defined in the CRT) is responsible
//!    for running the cleanup routines.
//! 4. Eventually the "catch" code in the `try` intrinsic (generated by the
//!    compiler) is executed and indicates that control should come back to
//!    Rust. This is done via a `catchswitch` plus a `catchpad` instruction in
//!    LLVM IR terms, finally returning normal control to the program with a
//!    `catchret` instruction.
//!
//! Some specific differences from the gcc-based exception handling are:
//!
//! * Rust has no custom personality function, it is instead *always*
//!   `__CxxFrameHandler3`. Additionally, no extra filtering is performed, so we
//!   end up catching any C++ exceptions that happen to look like the kind we're
//!   throwing. Note that throwing an exception into Rust is undefined behavior
//!   anyway, so this should be fine.
//! * We've got some data to transmit across the unwinding boundary,
//!   specifically a `Box<dyn Any + Send>`. Like with Dwarf exceptions
//!   these two pointers are stored as a payload in the exception itself. On
//!   MSVC, however, there's no need for an extra heap allocation because the
//!   call stack is preserved while filter functions are being executed. This
//!   means that the pointers are passed directly to `_CxxThrowException` which
//!   are then recovered in the filter function to be written to the stack frame
//!   of the `try` intrinsic.
//!
//! [win64]: https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64
//! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions

#![allow(nonstandard_style)]
#![allow(private_no_mangle_fns)]

use alloc::boxed::Box;
use core::any::Any;
use core::mem;
use core::raw;
use libc::{c_int, c_uint, c_void};

// First up, a whole bunch of type definitions. There's a few platform-specific
// oddities here, and a lot that's just blatantly copied from LLVM. The purpose
// of all this is to implement the `panic` function below through a call to
// `_CxxThrowException`.
//
// This function takes two arguments. The first is a pointer to the data we're
// passing in, which in this case is our trait object. Pretty easy to find! The
// next, however, is more complicated. This is a pointer to a `_ThrowInfo`
// structure, and it generally is just intended to just describe the exception
// being thrown.
//
// Currently the definition of this type [1] is a little hairy, and the main
// oddity (and difference from the online article) is that on 32-bit the
// pointers are pointers but on 64-bit the pointers are expressed as 32-bit
// offsets from the `__ImageBase` symbol. The `ptr_t` and `ptr!` macro in the
// modules below are used to express this.
//
// The maze of type definitions also closely follows what LLVM emits for this
// sort of operation. For example, if you compile this C++ code on MSVC and emit
// the LLVM IR:
//
//      #include <stdint.h>
//
//      struct rust_panic {
//          rust_panic(const rust_panic&);
//          ~rust_panic();
//
//          uint64_t x[2];
//      };
//
//      void foo() {
//          rust_panic a = {0, 1};
//          throw a;
//      }
//
// That's essentially what we're trying to emulate. Most of the constant values
// below were just copied from LLVM,
//
// In any case, these structures are all constructed in a similar manner, and
// it's just somewhat verbose for us.
//
// [1]: http://www.geoffchappell.com/studies/msvc/language/predefined/

#[cfg(target_arch = "x86")]
#[macro_use]
mod imp {
    pub type ptr_t = *mut u8;

    macro_rules! ptr {
        (0) => {
            core::ptr::null_mut()
        };
        ($e:expr) => {
            $e as *mut u8
        };
    }
}

#[cfg(any(target_arch = "x86_64", target_arch = "arm"))]
#[macro_use]
mod imp {
    pub type ptr_t = u32;

    extern "C" {
        pub static __ImageBase: u8;
    }

    macro_rules! ptr {
        (0) => (0);
        ($e:expr) => {
            (($e as usize) - (&imp::__ImageBase as *const _ as usize)) as u32
        }
    }
}

#[repr(C)]
pub struct _ThrowInfo {
    pub attributes: c_uint,
    pub pmfnUnwind: imp::ptr_t,
    pub pForwardCompat: imp::ptr_t,
    pub pCatchableTypeArray: imp::ptr_t,
}

#[repr(C)]
pub struct _CatchableTypeArray {
    pub nCatchableTypes: c_int,
    pub arrayOfCatchableTypes: [imp::ptr_t; 1],
}

#[repr(C)]
pub struct _CatchableType {
    pub properties: c_uint,
    pub pType: imp::ptr_t,
    pub thisDisplacement: _PMD,
    pub sizeOrOffset: c_int,
    pub copyFunction: imp::ptr_t,
}

#[repr(C)]
pub struct _PMD {
    pub mdisp: c_int,
    pub pdisp: c_int,
    pub vdisp: c_int,
}

#[repr(C)]
pub struct _TypeDescriptor {
    pub pVFTable: *const u8,
    pub spare: *mut u8,
    pub name: [u8; 11],
}

// Note that we intentionally ignore name mangling rules here: we don't want C++
// to be able to catch Rust panics by simply declaring a `struct rust_panic`.
const TYPE_NAME: [u8; 11] = *b"rust_panic\0";

static mut THROW_INFO: _ThrowInfo = _ThrowInfo {
    attributes: 0,
    pmfnUnwind: ptr!(0),
    pForwardCompat: ptr!(0),
    pCatchableTypeArray: ptr!(0),
};

static mut CATCHABLE_TYPE_ARRAY: _CatchableTypeArray =
    _CatchableTypeArray { nCatchableTypes: 1, arrayOfCatchableTypes: [ptr!(0)] };

static mut CATCHABLE_TYPE: _CatchableType = _CatchableType {
    properties: 0,
    pType: ptr!(0),
    thisDisplacement: _PMD { mdisp: 0, pdisp: -1, vdisp: 0 },
    sizeOrOffset: mem::size_of::<[u64; 2]>() as c_int,
    copyFunction: ptr!(0),
};

extern "C" {
    // The leading `\x01` byte here is actually a magical signal to LLVM to
    // *not* apply any other mangling like prefixing with a `_` character.
    //
    // This symbol is the vtable used by C++'s `std::type_info`. Objects of type
    // `std::type_info`, type descriptors, have a pointer to this table. Type
    // descriptors are referenced by the C++ EH structures defined above and
    // that we construct below.
    #[link_name = "\x01??_7type_info@@6B@"]
    static TYPE_INFO_VTABLE: *const u8;
}

// We use #[lang = "eh_catch_typeinfo"] here as this is the type descriptor which
// we'll use in LLVM's `catchpad` instruction which ends up also being passed as
// an argument to the C++ personality function.
//
// Again, I'm not entirely sure what this is describing, it just seems to work.
#[cfg_attr(not(test), lang = "eh_catch_typeinfo")]
static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
    pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _,
    spare: core::ptr::null_mut(),
    name: TYPE_NAME,
};

// Destructor used if the C++ code decides to capture the exception and drop it
// without propagating it. The catch part of the try intrinsic will set the
// first word of the exception object to 0 so that it is skipped by the
// destructor.
//
// Note that x86 Windows uses the "thiscall" calling convention for C++ member
// functions instead of the default "C" calling convention.
//
// The exception_copy function is a bit special here: it is invoked by the MSVC
// runtime under a try/catch block and the panic that we generate here will be
// used as the result of the exception copy. This is used by the C++ runtime to
// support capturing exceptions with std::exception_ptr, which we can't support
// because Box<dyn Any> isn't clonable.
cfg_if::cfg_if! {
    if #[cfg(target_arch = "x86")] {
        unsafe extern "thiscall" fn exception_cleanup(e: *mut [u64; 2]) {
            if (*e)[0] != 0 {
                cleanup(*e);
            }
        }
        #[unwind(allowed)]
        unsafe extern "thiscall" fn exception_copy(_dest: *mut [u64; 2],
                                                   _src: *mut [u64; 2])
                                                   -> *mut [u64; 2] {
            panic!("Rust panics cannot be copied");
        }
    } else {
        unsafe extern "C" fn exception_cleanup(e: *mut [u64; 2]) {
            if (*e)[0] != 0 {
                cleanup(*e);
            }
        }
        #[unwind(allowed)]
        unsafe extern "C" fn exception_copy(_dest: *mut [u64; 2],
                                            _src: *mut [u64; 2])
                                            -> *mut [u64; 2] {
            panic!("Rust panics cannot be copied");
        }
    }
}

pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
    use core::intrinsics::atomic_store;

    // _CxxThrowException executes entirely on this stack frame, so there's no
    // need to otherwise transfer `data` to the heap. We just pass a stack
    // pointer to this function.
    //
    // The first argument is the payload being thrown (our two pointers), and
    // the second argument is the type information object describing the
    // exception (constructed above).
    let ptrs = mem::transmute::<_, raw::TraitObject>(data);
    let mut ptrs = [ptrs.data as u64, ptrs.vtable as u64];
    let throw_ptr = ptrs.as_mut_ptr() as *mut _;

    // This... may seems surprising, and justifiably so. On 32-bit MSVC the
    // pointers between these structure are just that, pointers. On 64-bit MSVC,
    // however, the pointers between structures are rather expressed as 32-bit
    // offsets from `__ImageBase`.
    //
    // Consequently, on 32-bit MSVC we can declare all these pointers in the
    // `static`s above. On 64-bit MSVC, we would have to express subtraction of
    // pointers in statics, which Rust does not currently allow, so we can't
    // actually do that.
    //
    // The next best thing, then is to fill in these structures at runtime
    // (panicking is already the "slow path" anyway). So here we reinterpret all
    // of these pointer fields as 32-bit integers and then store the
    // relevant value into it (atomically, as concurrent panics may be
    // happening). Technically the runtime will probably do a nonatomic read of
    // these fields, but in theory they never read the *wrong* value so it
    // shouldn't be too bad...
    //
    // In any case, we basically need to do something like this until we can
    // express more operations in statics (and we may never be able to).
    if !cfg!(bootstrap) {
        atomic_store(
            &mut THROW_INFO.pmfnUnwind as *mut _ as *mut u32,
            ptr!(exception_cleanup) as u32,
        );
    }
    atomic_store(
        &mut THROW_INFO.pCatchableTypeArray as *mut _ as *mut u32,
        ptr!(&CATCHABLE_TYPE_ARRAY as *const _) as u32,
    );
    atomic_store(
        &mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0] as *mut _ as *mut u32,
        ptr!(&CATCHABLE_TYPE as *const _) as u32,
    );
    atomic_store(
        &mut CATCHABLE_TYPE.pType as *mut _ as *mut u32,
        ptr!(&TYPE_DESCRIPTOR as *const _) as u32,
    );
    if !cfg!(bootstrap) {
        atomic_store(
            &mut CATCHABLE_TYPE.copyFunction as *mut _ as *mut u32,
            ptr!(exception_copy) as u32,
        );
    }

    extern "system" {
        #[unwind(allowed)]
        pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
    }

    _CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
}

pub fn payload() -> [u64; 2] {
    [0; 2]
}

pub unsafe fn cleanup(payload: [u64; 2]) -> Box<dyn Any + Send> {
    mem::transmute(raw::TraitObject { data: payload[0] as *mut _, vtable: payload[1] as *mut _ })
}

// This is required by the compiler to exist (e.g., it's a lang item), but
// it's never actually called by the compiler because __C_specific_handler
// or _except_handler3 is the personality function that is always used.
// Hence this is just an aborting stub.
#[lang = "eh_personality"]
#[cfg(not(test))]
fn rust_eh_personality() {
    unsafe { core::intrinsics::abort() }
}
<|endoftext|>


<commit_before>// Copyright 2014 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 rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::mir::*;
use rustc::mir::transform::MirSource;
use rustc::ty::TyCtxt;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::{Idx};
use std::fmt::Display;
use std::fs;
use std::io::{self, Write};
use std::path::{PathBuf, Path};

const INDENT: &'static str = "    ";
/// Alignment for lining up comments following MIR statements
const ALIGN: usize = 40;

/// If the session is properly configured, dumps a human-readable
/// representation of the mir into:
///
/// ```text
/// rustc.node<node_id>.<pass_name>.<disambiguator>
/// ```
///
/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
/// where `<filter>` takes the following forms:
///
/// - `all` -- dump MIR for all fns, all passes, all everything
/// - `substring1&substring2,...` -- `&`-separated list of substrings
///   that can appear in the pass-name or the `item_path_str` for the given
///   node-id. If any one of the substrings match, the data is dumped out.
pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                          pass_name: &str,
                          disambiguator: &Display,
                          src: MirSource,
                          mir: &Mir<'tcx>) {
    let filters = match tcx.sess.opts.debugging_opts.dump_mir {
        None => return,
        Some(ref filters) => filters,
    };
    let node_id = src.item_id();
    let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id));
    let is_matched =
        filters.split("&")
               .any(|filter| {
                   filter == "all" ||
                       pass_name.contains(filter) ||
                       node_path.contains(filter)
               });
    if !is_matched {
        return;
    }

    let promotion_id = match src {
        MirSource::Promoted(_, id) => format!("-{:?}", id),
        _ => String::new()
    };

    let mut file_path = PathBuf::new();
    if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
        let p = Path::new(file_dir);
        file_path.push(p);
    };
    let file_name = format!("rustc.node{}{}.{}.{}.mir",
                            node_id, promotion_id, pass_name, disambiguator);
    file_path.push(&file_name);
    let _ = fs::File::create(&file_path).and_then(|mut file| {
        writeln!(file, "// MIR for `{}`", node_path)?;
        writeln!(file, "// node_id = {}", node_id)?;
        writeln!(file, "// pass_name = {}", pass_name)?;
        writeln!(file, "// disambiguator = {}", disambiguator)?;
        writeln!(file, "")?;
        write_mir_fn(tcx, src, mir, &mut file)?;
        Ok(())
    });
}

/// Write out a human-readable textual representation for the given MIR.
pub fn write_mir_pretty<'a, 'b, 'tcx, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
                                         iter: I,
                                         w: &mut Write)
                                         -> io::Result<()>
    where I: Iterator<Item=DefId>, 'tcx: 'a
{
    let mut first = true;
    for def_id in iter {
        let mir = &tcx.item_mir(def_id);

        if first {
            first = false;
        } else {
            // Put empty lines between all items
            writeln!(w, "")?;
        }

        let id = tcx.hir.as_local_node_id(def_id).unwrap();
        let src = MirSource::from_node(tcx, id);
        write_mir_fn(tcx, src, mir, w)?;

        for (i, mir) in mir.promoted.iter_enumerated() {
            writeln!(w, "")?;
            write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?;
        }
    }
    Ok(())
}

pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              src: MirSource,
                              mir: &Mir<'tcx>,
                              w: &mut Write)
                              -> io::Result<()> {
    write_mir_intro(tcx, src, mir, w)?;
    for block in mir.basic_blocks().indices() {
        write_basic_block(tcx, block, mir, w)?;
        if block.index() + 1 != mir.basic_blocks().len() {
            writeln!(w, "")?;
        }
    }

    writeln!(w, "}}")?;
    Ok(())
}

/// Write out a human-readable textual representation for the given basic block.
fn write_basic_block(tcx: TyCtxt,
                     block: BasicBlock,
                     mir: &Mir,
                     w: &mut Write)
                     -> io::Result<()> {
    let data = &mir[block];

    // Basic block label at the top.
    writeln!(w, "{}{:?}: {{", INDENT, block)?;

    // List of statements in the middle.
    let mut current_location = Location { block: block, statement_index: 0 };
    for statement in &data.statements {
        let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
        writeln!(w, "{0:1$} // {2}",
                 indented_mir,
                 ALIGN,
                 comment(tcx, statement.source_info))?;

        current_location.statement_index += 1;
    }

    // Terminator at the bottom.
    let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
    writeln!(w, "{0:1$} // {2}",
             indented_terminator,
             ALIGN,
             comment(tcx, data.terminator().source_info))?;

    writeln!(w, "{}}}", INDENT)
}

fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
    format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
}

/// Prints user-defined variables in a scope tree.
///
/// Returns the total number of variables printed.
fn write_scope_tree(tcx: TyCtxt,
                    mir: &Mir,
                    scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
                    w: &mut Write,
                    parent: VisibilityScope,
                    depth: usize)
                    -> io::Result<()> {
    let indent = depth * INDENT.len();

    let children = match scope_tree.get(&parent) {
        Some(childs) => childs,
        None => return Ok(()),
    };

    for &child in children {
        let data = &mir.visibility_scopes[child];
        assert_eq!(data.parent_scope, Some(parent));
        writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;

        // User variable types (including the user's name in a comment).
        for local in mir.vars_iter() {
            let var = &mir.local_decls[local];
            let (name, source_info) = if var.source_info.unwrap().scope == child {
                (var.name.unwrap(), var.source_info.unwrap())
            } else {
                // Not a variable or not declared in this scope.
                continue;
            };

            let mut_str = if var.mutability == Mutability::Mut {
                "mut "
            } else {
                ""
            };

            let indent = indent + INDENT.len();
            let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
                                       INDENT,
                                       indent,
                                       mut_str,
                                       local,
                                       var.ty);
            writeln!(w, "{0:1$} // \"{2}\" in {3}",
                     indented_var,
                     ALIGN,
                     name,
                     comment(tcx, source_info))?;
        }

        write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;

        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
    }

    Ok(())
}

/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
/// local variables (both user-defined bindings and compiler temporaries).
fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                             src: MirSource,
                             mir: &Mir,
                             w: &mut Write)
                             -> io::Result<()> {
    write_mir_sig(tcx, src, mir, w)?;
    writeln!(w, " {{")?;

    // construct a scope tree and write it out
    let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
    for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
        if let Some(parent) = scope_data.parent_scope {
            scope_tree.entry(parent)
                      .or_insert(vec![])
                      .push(VisibilityScope::new(index));
        } else {
            // Only the argument scope has no parent, because it's the root.
            assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
        }
    }

    // Print return pointer
    let indented_retptr = format!("{}let mut {:?}: {};",
                                  INDENT,
                                  RETURN_POINTER,
                                  mir.return_ty);
    writeln!(w, "{0:1$} // return pointer",
             indented_retptr,
             ALIGN)?;

    write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;

    write_temp_decls(mir, w)?;

    // Add an empty line before the first block is printed.
    writeln!(w, "")?;

    Ok(())
}

fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
                 -> io::Result<()>
{
    match src {
        MirSource::Fn(_) => write!(w, "fn")?,
        MirSource::Const(_) => write!(w, "const")?,
        MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
        MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
        MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
    }

    write!(w, " {}", tcx.node_path_str(src.item_id()))?;

    if let MirSource::Fn(_) = src {
        write!(w, "(")?;

        // fn argument types.
        for (i, arg) in mir.args_iter().enumerate() {
            if i != 0 {
                write!(w, ", ")?;
            }
            write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
        }

        write!(w, ") -> {}", mir.return_ty)
    } else {
        assert_eq!(mir.arg_count, 0);
        write!(w, ": {} =", mir.return_ty)
    }
}

fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
    // Compiler-introduced temporary types.
    for temp in mir.temps_iter() {
        writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
    }

    Ok(())
}
<commit_msg>Rollup merge of #39311 - solson:fix-unpretty-mir-non-local, r=eddyb<commit_after>// Copyright 2014 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 rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::mir::*;
use rustc::mir::transform::MirSource;
use rustc::ty::TyCtxt;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::{Idx};
use std::fmt::Display;
use std::fs;
use std::io::{self, Write};
use std::path::{PathBuf, Path};

const INDENT: &'static str = "    ";
/// Alignment for lining up comments following MIR statements
const ALIGN: usize = 40;

/// If the session is properly configured, dumps a human-readable
/// representation of the mir into:
///
/// ```text
/// rustc.node<node_id>.<pass_name>.<disambiguator>
/// ```
///
/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
/// where `<filter>` takes the following forms:
///
/// - `all` -- dump MIR for all fns, all passes, all everything
/// - `substring1&substring2,...` -- `&`-separated list of substrings
///   that can appear in the pass-name or the `item_path_str` for the given
///   node-id. If any one of the substrings match, the data is dumped out.
pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                          pass_name: &str,
                          disambiguator: &Display,
                          src: MirSource,
                          mir: &Mir<'tcx>) {
    let filters = match tcx.sess.opts.debugging_opts.dump_mir {
        None => return,
        Some(ref filters) => filters,
    };
    let node_id = src.item_id();
    let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id));
    let is_matched =
        filters.split("&")
               .any(|filter| {
                   filter == "all" ||
                       pass_name.contains(filter) ||
                       node_path.contains(filter)
               });
    if !is_matched {
        return;
    }

    let promotion_id = match src {
        MirSource::Promoted(_, id) => format!("-{:?}", id),
        _ => String::new()
    };

    let mut file_path = PathBuf::new();
    if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
        let p = Path::new(file_dir);
        file_path.push(p);
    };
    let file_name = format!("rustc.node{}{}.{}.{}.mir",
                            node_id, promotion_id, pass_name, disambiguator);
    file_path.push(&file_name);
    let _ = fs::File::create(&file_path).and_then(|mut file| {
        writeln!(file, "// MIR for `{}`", node_path)?;
        writeln!(file, "// node_id = {}", node_id)?;
        writeln!(file, "// pass_name = {}", pass_name)?;
        writeln!(file, "// disambiguator = {}", disambiguator)?;
        writeln!(file, "")?;
        write_mir_fn(tcx, src, mir, &mut file)?;
        Ok(())
    });
}

/// Write out a human-readable textual representation for the given MIR.
pub fn write_mir_pretty<'a, 'b, 'tcx, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
                                         iter: I,
                                         w: &mut Write)
                                         -> io::Result<()>
    where I: Iterator<Item=DefId>, 'tcx: 'a
{
    let mut first = true;
    for def_id in iter.filter(DefId::is_local) {
        let mir = &tcx.item_mir(def_id);

        if first {
            first = false;
        } else {
            // Put empty lines between all items
            writeln!(w, "")?;
        }

        let id = tcx.hir.as_local_node_id(def_id).unwrap();
        let src = MirSource::from_node(tcx, id);
        write_mir_fn(tcx, src, mir, w)?;

        for (i, mir) in mir.promoted.iter_enumerated() {
            writeln!(w, "")?;
            write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?;
        }
    }
    Ok(())
}

pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              src: MirSource,
                              mir: &Mir<'tcx>,
                              w: &mut Write)
                              -> io::Result<()> {
    write_mir_intro(tcx, src, mir, w)?;
    for block in mir.basic_blocks().indices() {
        write_basic_block(tcx, block, mir, w)?;
        if block.index() + 1 != mir.basic_blocks().len() {
            writeln!(w, "")?;
        }
    }

    writeln!(w, "}}")?;
    Ok(())
}

/// Write out a human-readable textual representation for the given basic block.
fn write_basic_block(tcx: TyCtxt,
                     block: BasicBlock,
                     mir: &Mir,
                     w: &mut Write)
                     -> io::Result<()> {
    let data = &mir[block];

    // Basic block label at the top.
    writeln!(w, "{}{:?}: {{", INDENT, block)?;

    // List of statements in the middle.
    let mut current_location = Location { block: block, statement_index: 0 };
    for statement in &data.statements {
        let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
        writeln!(w, "{0:1$} // {2}",
                 indented_mir,
                 ALIGN,
                 comment(tcx, statement.source_info))?;

        current_location.statement_index += 1;
    }

    // Terminator at the bottom.
    let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
    writeln!(w, "{0:1$} // {2}",
             indented_terminator,
             ALIGN,
             comment(tcx, data.terminator().source_info))?;

    writeln!(w, "{}}}", INDENT)
}

fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
    format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
}

/// Prints user-defined variables in a scope tree.
///
/// Returns the total number of variables printed.
fn write_scope_tree(tcx: TyCtxt,
                    mir: &Mir,
                    scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
                    w: &mut Write,
                    parent: VisibilityScope,
                    depth: usize)
                    -> io::Result<()> {
    let indent = depth * INDENT.len();

    let children = match scope_tree.get(&parent) {
        Some(childs) => childs,
        None => return Ok(()),
    };

    for &child in children {
        let data = &mir.visibility_scopes[child];
        assert_eq!(data.parent_scope, Some(parent));
        writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;

        // User variable types (including the user's name in a comment).
        for local in mir.vars_iter() {
            let var = &mir.local_decls[local];
            let (name, source_info) = if var.source_info.unwrap().scope == child {
                (var.name.unwrap(), var.source_info.unwrap())
            } else {
                // Not a variable or not declared in this scope.
                continue;
            };

            let mut_str = if var.mutability == Mutability::Mut {
                "mut "
            } else {
                ""
            };

            let indent = indent + INDENT.len();
            let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
                                       INDENT,
                                       indent,
                                       mut_str,
                                       local,
                                       var.ty);
            writeln!(w, "{0:1$} // \"{2}\" in {3}",
                     indented_var,
                     ALIGN,
                     name,
                     comment(tcx, source_info))?;
        }

        write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;

        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
    }

    Ok(())
}

/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
/// local variables (both user-defined bindings and compiler temporaries).
fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                             src: MirSource,
                             mir: &Mir,
                             w: &mut Write)
                             -> io::Result<()> {
    write_mir_sig(tcx, src, mir, w)?;
    writeln!(w, " {{")?;

    // construct a scope tree and write it out
    let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
    for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
        if let Some(parent) = scope_data.parent_scope {
            scope_tree.entry(parent)
                      .or_insert(vec![])
                      .push(VisibilityScope::new(index));
        } else {
            // Only the argument scope has no parent, because it's the root.
            assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
        }
    }

    // Print return pointer
    let indented_retptr = format!("{}let mut {:?}: {};",
                                  INDENT,
                                  RETURN_POINTER,
                                  mir.return_ty);
    writeln!(w, "{0:1$} // return pointer",
             indented_retptr,
             ALIGN)?;

    write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;

    write_temp_decls(mir, w)?;

    // Add an empty line before the first block is printed.
    writeln!(w, "")?;

    Ok(())
}

fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
                 -> io::Result<()>
{
    match src {
        MirSource::Fn(_) => write!(w, "fn")?,
        MirSource::Const(_) => write!(w, "const")?,
        MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
        MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
        MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
    }

    write!(w, " {}", tcx.node_path_str(src.item_id()))?;

    if let MirSource::Fn(_) = src {
        write!(w, "(")?;

        // fn argument types.
        for (i, arg) in mir.args_iter().enumerate() {
            if i != 0 {
                write!(w, ", ")?;
            }
            write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
        }

        write!(w, ") -> {}", mir.return_ty)
    } else {
        assert_eq!(mir.arg_count, 0);
        write!(w, ": {} =", mir.return_ty)
    }
}

fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
    // Compiler-introduced temporary types.
    for temp in mir.temps_iter() {
        writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>
//! All entities of a gerrit instance
//!
//! The entities are documented on gerrit site on
//! <https://gerrit-documentation.storage.googleapis.com/Documentation/2.12.3/rest-api-changes.html#json-entities>.
//!
//! **NOTICE**: Only current needed entities are here reflected.

use std::collections::HashMap;

#[derive(Deserialize, Debug, Clone)]
pub struct AccountInfo {
    pub _account_id: Option<u64>,
    pub name: Option<String>,
    pub email: Option<String>,
    pub secondary_emails: Option<Vec<String>>,
    pub username: Option<String>,
    pub _more_accounts: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ActionInfo {
    pub method: Option<String>,
    pub label: Option<String>,
    pub title: Option<String>,
    pub enabled: Option<String>,
}

// the enum variants must be in upper case letters, the server will send them in this style
#[derive(Deserialize, Debug, Clone)]
pub enum ChangeStatus {
    NEW,
    MERGED,
    ABANDONED,
    DRAFT,
}

#[derive(Deserialize, Debug, Clone)]
pub struct LabelInfo {
    pub optional: Option<bool>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ChangeMessageInfo {
    pub id: String,
    pub author: Option<AccountInfo>,
    pub date: String,
    pub message: String,
    pub tag: Option<String>,
    pub _revision_number: Option<u16>,
}

#[allow(non_camel_case_types)]
#[derive(Deserialize, Debug, Clone)]
pub enum RevisionInfoKind {
    REWORK,
    TRIVIAL_REBASE,
    MERGE_FIRST_PARENT_UPDATE,
    NO_CODE_CHANGE,
    NO_CHANGE,
}

#[derive(Deserialize, Debug, Clone)]
pub struct FetchInfo {
    pub url: String,
    #[serde(rename="ref")] // "ref" is a keyword
    pub reference: String,
    pub commands: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct GitPersonInfo {
    pub name: String,
    pub email: String,
    pub date: String,
    pub tz: u16,
}

#[derive(Deserialize, Debug, Clone)]
pub struct CommitInfoParents {
    pub commit: String,
    pub author: Option<GitPersonInfo>,
    pub committer: Option<GitPersonInfo>,
    pub subject: String,
    pub message: Option<String>,
    pub web_links: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct CommitInfo {
    pub commit: Option<String>,
    pub parents: Option<Vec<CommitInfoParents>>,
    pub author: Option<GitPersonInfo>,
    pub committer: Option<GitPersonInfo>,
    pub subject: Option<String>,
    pub message: Option<String>,
    pub web_links: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct RevisionInfo {
    pub draft: Option<bool>,
    pub has_draft_comments: Option<bool>,
    pub _number: u64,
    pub created: Option<String>,
    pub uploader: Option<AccountInfo>,
    #[serde(rename="ref")] // "ref" is a keyword
    pub reference: Option<String>,
    pub fetch: HashMap<String, FetchInfo>,
    pub commit: Option<CommitInfo>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ProblemInfo {
    pub message: String,
    pub status: Option<String>,
    pub outcome: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub enum ProjectState {
    ACTIVE,
    READONLY,
    HIDDEN,
}

#[derive(Deserialize, Debug, Clone)]
pub enum ProjectTypes {
    ALL,
    CODE,
    PERMISSIONS,
}

#[derive(Deserialize, Debug, Clone)]
pub struct WebLinkInfo {
    pub name: String,
    pub url: String,
    pub image_url: String,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ProjectInfo {
    pub name: Option<String>,
    pub id: String,
    pub parent: Option<String>,
    pub description: Option<String>,
    pub state: Option<ProjectState>,
    pub branches: Option<HashMap<String, String>>,
    pub web_links: Option<Vec<WebLinkInfo>>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ChangeInfo {
    pub id: String,
    pub project: String,
    pub branch: String,
    pub topic: Option<String>,
    pub change_id: String,
    pub subject: String,
    pub status: ChangeStatus,
    pub created: String,
    pub updated: String,
    pub submitted: Option<String>,
    pub starred: Option<bool>,
    pub stars: Option<Vec<String>>,
    pub reviewed: Option<bool>,
    pub submit_type: Option<String>,
    pub mergeable: Option<bool>,
    pub insertions: u16,
    pub deletions: u16,
    pub _number: u64,
    pub owner: AccountInfo,
    pub action: Option<Vec<ActionInfo>>,
    pub labels: Option<HashMap<String, LabelInfo>>,
    pub permitted_labels: Option<HashMap<String, LabelInfo>>,
    pub removeable_reviewers: Option<Vec<AccountInfo>>,
    pub reviewers: Option<String>,
    pub messages: Option<HashMap<String, ChangeMessageInfo>>,
    pub current_revision: Option<String>,
    pub revision: Option<HashMap<String, RevisionInfo>>,
    pub revisions: Option<HashMap<String, RevisionInfo>>,
    pub _more_changes: Option<bool>,
    pub problems: Option<Vec<ProblemInfo>>,
}
<commit_msg>add ChangeInput and MergeInput entities<commit_after>
//! All entities of a gerrit instance
//!
//! The entities are documented on gerrit site on
//! <https://gerrit-documentation.storage.googleapis.com/Documentation/2.12.3/rest-api-changes.html#json-entities>.
//!
//! **NOTICE**: Only current needed entities are here reflected.

use std::collections::HashMap;

#[derive(Deserialize, Debug, Clone)]
pub struct AccountInfo {
    pub _account_id: Option<u64>,
    pub name: Option<String>,
    pub email: Option<String>,
    pub secondary_emails: Option<Vec<String>>,
    pub username: Option<String>,
    pub _more_accounts: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ActionInfo {
    pub method: Option<String>,
    pub label: Option<String>,
    pub title: Option<String>,
    pub enabled: Option<String>,
}

// the enum variants must be in upper case letters, the server will send them in this style
#[derive(Deserialize, Debug, Clone)]
pub enum ChangeStatus {
    NEW,
    MERGED,
    ABANDONED,
    DRAFT,
}

#[derive(Deserialize, Debug, Clone)]
pub struct LabelInfo {
    pub optional: Option<bool>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ChangeMessageInfo {
    pub id: String,
    pub author: Option<AccountInfo>,
    pub date: String,
    pub message: String,
    pub tag: Option<String>,
    pub _revision_number: Option<u16>,
}

#[allow(non_camel_case_types)]
#[derive(Deserialize, Debug, Clone)]
pub enum RevisionInfoKind {
    REWORK,
    TRIVIAL_REBASE,
    MERGE_FIRST_PARENT_UPDATE,
    NO_CODE_CHANGE,
    NO_CHANGE,
}

#[derive(Deserialize, Debug, Clone)]
pub struct FetchInfo {
    pub url: String,
    #[serde(rename="ref")] // "ref" is a keyword
    pub reference: String,
    pub commands: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct GitPersonInfo {
    pub name: String,
    pub email: String,
    pub date: String,
    pub tz: u16,
}

#[derive(Deserialize, Debug, Clone)]
pub struct CommitInfoParents {
    pub commit: String,
    pub author: Option<GitPersonInfo>,
    pub committer: Option<GitPersonInfo>,
    pub subject: String,
    pub message: Option<String>,
    pub web_links: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct CommitInfo {
    pub commit: Option<String>,
    pub parents: Option<Vec<CommitInfoParents>>,
    pub author: Option<GitPersonInfo>,
    pub committer: Option<GitPersonInfo>,
    pub subject: Option<String>,
    pub message: Option<String>,
    pub web_links: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct RevisionInfo {
    pub draft: Option<bool>,
    pub has_draft_comments: Option<bool>,
    pub _number: u64,
    pub created: Option<String>,
    pub uploader: Option<AccountInfo>,
    #[serde(rename="ref")] // "ref" is a keyword
    pub reference: Option<String>,
    pub fetch: HashMap<String, FetchInfo>,
    pub commit: Option<CommitInfo>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ProblemInfo {
    pub message: String,
    pub status: Option<String>,
    pub outcome: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub enum ProjectState {
    ACTIVE,
    READONLY,
    HIDDEN,
}

#[derive(Deserialize, Debug, Clone)]
pub enum ProjectTypes {
    ALL,
    CODE,
    PERMISSIONS,
}

#[derive(Deserialize, Debug, Clone)]
pub struct WebLinkInfo {
    pub name: String,
    pub url: String,
    pub image_url: String,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ProjectInfo {
    pub name: Option<String>,
    pub id: String,
    pub parent: Option<String>,
    pub description: Option<String>,
    pub state: Option<ProjectState>,
    pub branches: Option<HashMap<String, String>>,
    pub web_links: Option<Vec<WebLinkInfo>>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct ChangeInfo {
    pub id: String,
    pub project: String,
    pub branch: String,
    pub topic: Option<String>,
    pub change_id: String,
    pub subject: String,
    pub status: ChangeStatus,
    pub created: String,
    pub updated: String,
    pub submitted: Option<String>,
    pub starred: Option<bool>,
    pub stars: Option<Vec<String>>,
    pub reviewed: Option<bool>,
    pub submit_type: Option<String>,
    pub mergeable: Option<bool>,
    pub insertions: u16,
    pub deletions: u16,
    pub _number: u64,
    pub owner: AccountInfo,
    pub action: Option<Vec<ActionInfo>>,
    pub labels: Option<HashMap<String, LabelInfo>>,
    pub permitted_labels: Option<HashMap<String, LabelInfo>>,
    pub removeable_reviewers: Option<Vec<AccountInfo>>,
    pub reviewers: Option<String>,
    pub messages: Option<HashMap<String, ChangeMessageInfo>>,
    pub current_revision: Option<String>,
    pub revision: Option<HashMap<String, RevisionInfo>>,
    pub revisions: Option<HashMap<String, RevisionInfo>>,
    pub _more_changes: Option<bool>,
    pub problems: Option<Vec<ProblemInfo>>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ChangeInput {
    pub project: String,
    pub branch: String,
    pub subject: String,
    pub topic: Option<String>,
    // TODO: Only NEW and DRAFT allowed
    pub status: Option<String>,
    pub base_change: Option<String>,
    pub new_branch: Option<bool>,
    pub merge: Option<MergeInput>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MergeInput {
    pub source: String,
    // TODO: only recursive, resolve, simple-two-way-in-core, ours or theirs allowed
    pub strategy: Option<String>,
}
<|endoftext|>


<commit_before>use grammars::pmcfg::PMCFGRule;
use grammars::pmcfg::VarT;
use std::hash::Hash;
use std::collections::HashMap;

mod conversion;
mod from_str;
pub mod from_discodop;
pub mod from_rparse;
pub mod csparsing;

/// A linear context-free rewriting system.
#[derive(Debug, Clone)]
pub struct Lcfrs<N, T, W> {
    rules: Vec<PMCFGRule<N, T, W>>,
    init: N,
}

impl<N, T, W> Lcfrs<N, T, W>
where
    N: Hash + Eq,
{
    /// Instantiates a new LCFRS from a list of rules.
    /// The rules and nonterminals are checked for consistency with regards to
    /// * consistent fanouts,
    /// * linear and non-deleting compositions, and
    /// * fanout 1 for initial nonterminals.
    pub fn new(rules: Vec<PMCFGRule<N, T, W>>, init: N) -> Option<Self> {
        {
            // scope for lieftime of `fanouts` that borrows from rules
            let fanouts = read_fanouts(&rules)?;

            // check initial fanout
            if fanouts.get(&&init) != Some(&1) {
                return None;
            }

            // check variables
            if !rules.iter().all(|rule| {
                let fanouts: Option<Vec<usize>> = rule.tail
                    .iter()
                    .map(|nt| fanouts.get(nt).map(|u| *u))
                    .collect();
                if let Some(fanouts) = fanouts {
                    check_composition(&rule.composition.composition, &fanouts)
                } else {
                    false
                }
            })
            {
                return None;
            }
        }

        Some(Lcfrs { rules, init })
    }
}

impl<N, T, W> Lcfrs<N, T, W> {
    /// Deconstructs the data type into its parts.
    pub fn destruct(self) -> (Vec<PMCFGRule<N, T, W>>, N) {
        (self.rules, self.init)
    }
}

/// Reads the fanout off `PMCFGRules`.
/// Will return `None` if rule fanouts are inconsistent for same nonterminals.
fn read_fanouts<'a, R, N, T, W>(rules: R) -> Option<HashMap<&'a N, usize>>
where
    R: IntoIterator<Item = &'a PMCFGRule<N, T, W>>,
    N: 'a + Hash + Eq,
    T: 'a,
    W: 'a,
{
    let mut fanouts: HashMap<&N, usize> = HashMap::new();
    for rule in rules {
        if fanouts.entry(&rule.head).or_insert_with(|| {
            rule.composition.composition.len()
        }) != &rule.composition.composition.len()
        {
            return None;
        }
    }
    Some(fanouts)
}

/// Checks a composition for linearity.
/// Will return true if each variable occurs exactly once according to the given fanouts.
fn check_composition<T>(composition: &Vec<Vec<VarT<T>>>, fanouts: &[usize]) -> bool {
    use util::vec_entry;

    let mut variable_occurances: Vec<Vec<usize>> = Vec::new();
    for (i, j) in composition
        .iter()
        .flat_map(|component| component.iter())
        .filter_map(|symbol| match *symbol {
            VarT::Var(i, j) => Some((i, j)),
            _ => None,
        })
    {
        vec_entry(&mut variable_occurances, i).push(j);
    }

    variable_occurances.len() == fanouts.len() &&
        variable_occurances.into_iter().enumerate().all(
            |(i, mut js)| {
                js.sort();
                let js_: Vec<usize> = (0..fanouts[i]).collect();
                js == js_
            },
        )
}

#[cfg(test)]
mod tests {
    use super::*;
    use grammars::pmcfg::{Composition, VarT};
    use std::collections::BTreeSet;
    use grammars::mcfg::Mcfg;

    #[test]
    fn fanouts() {
        assert_eq!(
            read_fanouts(&lcfrs_rules()),
            Some(vec![(&1, 1), (&2, 2)].into_iter().collect())
        );

        assert_eq!(read_fanouts(&inconsistent_rules()), None);
    }

    #[test]
    fn lcfrs_checks() {
        assert!(Lcfrs::new(lcfrs_rules(), 1).is_some());
        assert!(Lcfrs::new(inconsistent_rules(), 1).is_none());
        assert!(Lcfrs::new(mcfg_rules(), 1).is_none());
    }

    #[test]
    fn conversion() {
        let lcfrs: Lcfrs<(usize, BTreeSet<usize>), usize, ()> = Mcfg::new(mcfg_rules(), 1).into();

        let emptyset = BTreeSet::new();
        let set0: BTreeSet<_> = vec![0].into_iter().collect();
        let set01: BTreeSet<_> = vec![0, 1].into_iter().collect();

        assert_eq!(
            lcfrs.init,
            (1, BTreeSet::new())
        );

        let rules: Vec<PMCFGRule<(usize, BTreeSet<usize>), usize, ()>> =
            vec![
                PMCFGRule {
                    weight: (),
                    head: (1, emptyset.clone()),
                    tail: vec![],
                    composition: Composition { composition: vec![vec![VarT::T(0)]] },
                },
                PMCFGRule {
                    weight: (),
                    head: (1, emptyset.clone()),
                    tail: vec![(2, set0.clone())],
                    composition: Composition { composition: vec![vec![VarT::Var(0, 0), VarT::T(0)]] },
                },
                PMCFGRule {
                    weight: (),
                    head: (2, set0.clone()),
                    tail: vec![(2, set01.clone())],
                    composition: Composition { composition: vec![vec![VarT::T(2)]] },
                },
                PMCFGRule {
                    weight: (),
                    head: (2, set01.clone()),
                    tail: vec![(2, set01.clone())],
                    composition: Composition { composition: vec![] },
                },
            ];

        assert_eq!(
            lcfrs.rules.into_iter().collect::<BTreeSet<_>>(),
            rules.into_iter().collect::<BTreeSet<_>>()
        )
    }

    fn mcfg_rules() -> Vec<PMCFGRule<usize, usize, ()>> {
        vec![
            PMCFGRule {
                weight: (),
                head: 2,
                tail: vec![2],
                composition: Composition { composition: vec![vec![VarT::T(1)], vec![VarT::T(2)]] },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![2],
                composition: Composition { composition: vec![vec![VarT::Var(0, 1), VarT::T(0)]] },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![],
                composition: Composition { composition: vec![vec![VarT::T(0)]] },
            },
        ]
    }

    fn inconsistent_rules() -> Vec<PMCFGRule<usize, usize, ()>> {
        vec![
            PMCFGRule {
                weight: (),
                head: 2,
                tail: vec![2],
                composition: Composition { composition: vec![vec![VarT::T(1)], vec![VarT::T(2)]] },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![2],
                composition: Composition {
                    composition: vec![vec![VarT::Var(0, 0)], vec![VarT::Var(0, 1)]],
                },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![],
                composition: Composition { composition: vec![vec![VarT::T(0)]] },
            },
        ]
    }

    fn lcfrs_rules() -> Vec<PMCFGRule<usize, usize, ()>> {
        vec![
            PMCFGRule {
                weight: (),
                head: 2,
                tail: vec![],
                composition: Composition { composition: vec![vec![VarT::T(1)], vec![VarT::T(2)]] },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![2],
                composition: Composition {
                    composition: vec![vec![VarT::Var(0, 0), VarT::Var(0, 1)]],
                },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![1],
                composition: Composition { composition: vec![vec![VarT::Var(0, 0)]] },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![],
                composition: Composition { composition: vec![vec![VarT::T(0)]] },
            },
        ]
    }
}
<commit_msg>src/grammars/lcfrs/mod.rs: impl Display for Lcfrs<N, T, W><commit_after>use grammars::pmcfg::PMCFGRule;
use grammars::pmcfg::VarT;
use std::hash::Hash;
use std::collections::HashMap;

mod conversion;
mod from_str;
pub mod from_discodop;
pub mod from_rparse;
pub mod csparsing;

/// A linear context-free rewriting system.
#[derive(Debug, Clone)]
pub struct Lcfrs<N, T, W> {
    rules: Vec<PMCFGRule<N, T, W>>,
    init: N,
}

impl<N, T, W> Lcfrs<N, T, W>
where
    N: Hash + Eq,
{
    /// Instantiates a new LCFRS from a list of rules.
    /// The rules and nonterminals are checked for consistency with regards to
    /// * consistent fanouts,
    /// * linear and non-deleting compositions, and
    /// * fanout 1 for initial nonterminals.
    pub fn new(rules: Vec<PMCFGRule<N, T, W>>, init: N) -> Option<Self> {
        {
            // scope for lieftime of `fanouts` that borrows from rules
            let fanouts = read_fanouts(&rules)?;

            // check initial fanout
            if fanouts.get(&&init) != Some(&1) {
                return None;
            }

            // check variables
            if !rules.iter().all(|rule| {
                let fanouts: Option<Vec<usize>> = rule.tail
                    .iter()
                    .map(|nt| fanouts.get(nt).map(|u| *u))
                    .collect();
                if let Some(fanouts) = fanouts {
                    check_composition(&rule.composition.composition, &fanouts)
                } else {
                    false
                }
            })
            {
                return None;
            }
        }

        Some(Lcfrs { rules, init })
    }
}

impl<N, T, W> Lcfrs<N, T, W> {
    /// Deconstructs the data type into its parts.
    pub fn destruct(self) -> (Vec<PMCFGRule<N, T, W>>, N) {
        (self.rules, self.init)
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for Lcfrs<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        buffer.push_str(format!("initial: [{}]\n\n", self.init).as_str());

        for r in &self.rules {
            buffer.push_str(format!("{}\n", r).as_str());
        }

        write!(f, "{}", buffer)
    }
}

/// Reads the fanout off `PMCFGRules`.
/// Will return `None` if rule fanouts are inconsistent for same nonterminals.
fn read_fanouts<'a, R, N, T, W>(rules: R) -> Option<HashMap<&'a N, usize>>
where
    R: IntoIterator<Item = &'a PMCFGRule<N, T, W>>,
    N: 'a + Hash + Eq,
    T: 'a,
    W: 'a,
{
    let mut fanouts: HashMap<&N, usize> = HashMap::new();
    for rule in rules {
        if fanouts.entry(&rule.head).or_insert_with(|| {
            rule.composition.composition.len()
        }) != &rule.composition.composition.len()
        {
            return None;
        }
    }
    Some(fanouts)
}

/// Checks a composition for linearity.
/// Will return true if each variable occurs exactly once according to the given fanouts.
fn check_composition<T>(composition: &Vec<Vec<VarT<T>>>, fanouts: &[usize]) -> bool {
    use util::vec_entry;

    let mut variable_occurances: Vec<Vec<usize>> = Vec::new();
    for (i, j) in composition
        .iter()
        .flat_map(|component| component.iter())
        .filter_map(|symbol| match *symbol {
            VarT::Var(i, j) => Some((i, j)),
            _ => None,
        })
    {
        vec_entry(&mut variable_occurances, i).push(j);
    }

    variable_occurances.len() == fanouts.len() &&
        variable_occurances.into_iter().enumerate().all(
            |(i, mut js)| {
                js.sort();
                let js_: Vec<usize> = (0..fanouts[i]).collect();
                js == js_
            },
        )
}

#[cfg(test)]
mod tests {
    use super::*;
    use grammars::pmcfg::{Composition, VarT};
    use std::collections::BTreeSet;
    use grammars::mcfg::Mcfg;

    #[test]
    fn fanouts() {
        assert_eq!(
            read_fanouts(&lcfrs_rules()),
            Some(vec![(&1, 1), (&2, 2)].into_iter().collect())
        );

        assert_eq!(read_fanouts(&inconsistent_rules()), None);
    }

    #[test]
    fn lcfrs_checks() {
        assert!(Lcfrs::new(lcfrs_rules(), 1).is_some());
        assert!(Lcfrs::new(inconsistent_rules(), 1).is_none());
        assert!(Lcfrs::new(mcfg_rules(), 1).is_none());
    }

    #[test]
    fn conversion() {
        let lcfrs: Lcfrs<(usize, BTreeSet<usize>), usize, ()> = Mcfg::new(mcfg_rules(), 1).into();

        let emptyset = BTreeSet::new();
        let set0: BTreeSet<_> = vec![0].into_iter().collect();
        let set01: BTreeSet<_> = vec![0, 1].into_iter().collect();

        assert_eq!(
            lcfrs.init,
            (1, BTreeSet::new())
        );

        let rules: Vec<PMCFGRule<(usize, BTreeSet<usize>), usize, ()>> =
            vec![
                PMCFGRule {
                    weight: (),
                    head: (1, emptyset.clone()),
                    tail: vec![],
                    composition: Composition { composition: vec![vec![VarT::T(0)]] },
                },
                PMCFGRule {
                    weight: (),
                    head: (1, emptyset.clone()),
                    tail: vec![(2, set0.clone())],
                    composition: Composition { composition: vec![vec![VarT::Var(0, 0), VarT::T(0)]] },
                },
                PMCFGRule {
                    weight: (),
                    head: (2, set0.clone()),
                    tail: vec![(2, set01.clone())],
                    composition: Composition { composition: vec![vec![VarT::T(2)]] },
                },
                PMCFGRule {
                    weight: (),
                    head: (2, set01.clone()),
                    tail: vec![(2, set01.clone())],
                    composition: Composition { composition: vec![] },
                },
            ];

        assert_eq!(
            lcfrs.rules.into_iter().collect::<BTreeSet<_>>(),
            rules.into_iter().collect::<BTreeSet<_>>()
        )
    }

    fn mcfg_rules() -> Vec<PMCFGRule<usize, usize, ()>> {
        vec![
            PMCFGRule {
                weight: (),
                head: 2,
                tail: vec![2],
                composition: Composition { composition: vec![vec![VarT::T(1)], vec![VarT::T(2)]] },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![2],
                composition: Composition { composition: vec![vec![VarT::Var(0, 1), VarT::T(0)]] },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![],
                composition: Composition { composition: vec![vec![VarT::T(0)]] },
            },
        ]
    }

    fn inconsistent_rules() -> Vec<PMCFGRule<usize, usize, ()>> {
        vec![
            PMCFGRule {
                weight: (),
                head: 2,
                tail: vec![2],
                composition: Composition { composition: vec![vec![VarT::T(1)], vec![VarT::T(2)]] },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![2],
                composition: Composition {
                    composition: vec![vec![VarT::Var(0, 0)], vec![VarT::Var(0, 1)]],
                },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![],
                composition: Composition { composition: vec![vec![VarT::T(0)]] },
            },
        ]
    }

    fn lcfrs_rules() -> Vec<PMCFGRule<usize, usize, ()>> {
        vec![
            PMCFGRule {
                weight: (),
                head: 2,
                tail: vec![],
                composition: Composition { composition: vec![vec![VarT::T(1)], vec![VarT::T(2)]] },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![2],
                composition: Composition {
                    composition: vec![vec![VarT::Var(0, 0), VarT::Var(0, 1)]],
                },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![1],
                composition: Composition { composition: vec![vec![VarT::Var(0, 0)]] },
            },
            PMCFGRule {
                weight: (),
                head: 1,
                tail: vec![],
                composition: Composition { composition: vec![vec![VarT::T(0)]] },
            },
        ]
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use kvproto::import_sstpb::*;
use rocksdb::DB;

use super::Result;

pub struct ImportModeSwitcher {
    mode: SwitchMode,
    backup_options: Vec<(String, ImportModeOptions)>,
}

impl ImportModeSwitcher {
    pub fn new() -> ImportModeSwitcher {
        ImportModeSwitcher {
            mode: SwitchMode::Normal,
            backup_options: Vec::new(),
        }
    }

    pub fn enter_normal_mode(&mut self, db: &DB) -> Result<()> {
        if self.mode == SwitchMode::Normal {
            return Ok(());
        }

        for (cf_name, cf_opts) in &self.backup_options {
            cf_opts.set_options_cf(db, cf_name)?;
        }

        self.mode = SwitchMode::Normal;
        Ok(())
    }

    pub fn enter_import_mode(&mut self, db: &DB) -> Result<()> {
        if self.mode == SwitchMode::Import {
            return Ok(());
        }

        let import_options = ImportModeOptions::new();
        self.backup_options.clear();
        for cf_name in db.cf_names() {
            let cf_opts = ImportModeOptions::new_options_cf(db, cf_name);
            self.backup_options.push((cf_name.to_owned(), cf_opts));
            import_options.set_options_cf(db, cf_name)?;
        }

        self.mode = SwitchMode::Import;
        Ok(())
    }
}

struct ImportModeOptions {
    block_cache_size: u64,
    disable_auto_compactions: bool,
    level0_stop_writes_trigger: u32,
    level0_slowdown_writes_trigger: u32,
    soft_pending_compaction_bytes_limit: u64,
    hard_pending_compaction_bytes_limit: u64,
}

impl ImportModeOptions {
    fn new() -> ImportModeOptions {
        ImportModeOptions {
            block_cache_size: 1 << 30,
            disable_auto_compactions: true,
            level0_stop_writes_trigger: 1 << 30,
            level0_slowdown_writes_trigger: 1 << 30,
            soft_pending_compaction_bytes_limit: 0,
            hard_pending_compaction_bytes_limit: 0,
        }
    }

    fn new_options_cf(db: &DB, cf_name: &str) -> ImportModeOptions {
        let cf = db.cf_handle(cf_name).unwrap();
        let cf_opts = db.get_options_cf(cf);

        ImportModeOptions {
            block_cache_size: cf_opts.get_block_cache_capacity(),
            disable_auto_compactions: cf_opts.get_disable_auto_compactions(),
            level0_stop_writes_trigger: cf_opts.get_level_zero_stop_writes_trigger(),
            level0_slowdown_writes_trigger: cf_opts.get_level_zero_slowdown_writes_trigger(),
            soft_pending_compaction_bytes_limit: cf_opts.get_soft_pending_compaction_bytes_limit(),
            hard_pending_compaction_bytes_limit: cf_opts.get_hard_pending_compaction_bytes_limit(),
        }
    }

    fn set_options_cf(&self, db: &DB, cf_name: &str) -> Result<()> {
        let cf = db.cf_handle(cf_name).unwrap();
        let cf_opts = db.get_options_cf(cf);
        cf_opts.set_block_cache_capacity(self.block_cache_size)?;

        let opts = [
            (
                "disable_auto_compactions".to_owned(),
                self.disable_auto_compactions.to_string(),
            ),
            (
                "level0_stop_writes_trigger".to_owned(),
                self.level0_stop_writes_trigger.to_string(),
            ),
            (
                "level0_slowdown_writes_trigger".to_owned(),
                self.level0_slowdown_writes_trigger.to_string(),
            ),
            (
                "soft_pending_compaction_bytes_limit".to_owned(),
                self.soft_pending_compaction_bytes_limit.to_string(),
            ),
            (
                "hard_pending_compaction_bytes_limit".to_owned(),
                self.hard_pending_compaction_bytes_limit.to_string(),
            ),
        ];

        let tmp_opts: Vec<_> = opts.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
        db.set_options_cf(cf, tmp_opts.as_slice())?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::util::rocksdb_util::new_engine;
    use tempdir::TempDir;

    fn check_import_options(db: &DB, opts: &ImportModeOptions) {
        for cf_name in db.cf_names() {
            let cf = db.cf_handle(cf_name).unwrap();
            let cf_opts = db.get_options_cf(cf);
            assert_eq!(cf_opts.get_block_cache_capacity(), opts.block_cache_size);
            assert_eq!(
                cf_opts.get_disable_auto_compactions(),
                opts.disable_auto_compactions
            );
            assert_eq!(
                cf_opts.get_level_zero_stop_writes_trigger(),
                opts.level0_stop_writes_trigger
            );
            assert_eq!(
                cf_opts.get_level_zero_slowdown_writes_trigger(),
                opts.level0_slowdown_writes_trigger
            );
            // TODO: https://github.com/facebook/rocksdb/pull/3823
            // These options are set correctly, but we can't get them
            // because of the issue above.
            // assert_eq!(
            //     cf_opts.get_soft_pending_compaction_bytes_limit(),
            //     opts.soft_pending_compaction_bytes_limit
            // );
            // assert_eq!(
            //     cf_opts.get_hard_pending_compaction_bytes_limit(),
            //     opts.hard_pending_compaction_bytes_limit
            // );
        }
    }

    #[test]
    fn test_import_mode_switcher() {
        let temp_dir = TempDir::new("test_import_mode_switcher").unwrap();
        let db = new_engine(temp_dir.path().to_str().unwrap(), &["a", "b"], None).unwrap();

        let import_options = ImportModeOptions::new();
        let normal_options = ImportModeOptions::new_options_cf(&db, "default");

        let mut switcher = ImportModeSwitcher::new();
        check_import_options(&db, &normal_options);
        switcher.enter_import_mode(&db).unwrap();
        check_import_options(&db, &import_options);
        switcher.enter_import_mode(&db).unwrap();
        check_import_options(&db, &import_options);
        switcher.enter_normal_mode(&db).unwrap();
        check_import_options(&db, &normal_options);
        switcher.enter_normal_mode(&db).unwrap();
        check_import_options(&db, &normal_options);
    }
}
<commit_msg>import: enable auto compactions in import mode (#4086)<commit_after>// Copyright 2018 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use kvproto::import_sstpb::*;
use rocksdb::DB;

use super::Result;

pub struct ImportModeSwitcher {
    mode: SwitchMode,
    backup_db_options: ImportModeDBOptions,
    backup_cf_options: Vec<(String, ImportModeCFOptions)>,
}

impl ImportModeSwitcher {
    pub fn new() -> ImportModeSwitcher {
        ImportModeSwitcher {
            mode: SwitchMode::Normal,
            backup_db_options: ImportModeDBOptions::new(),
            backup_cf_options: Vec::new(),
        }
    }

    pub fn enter_normal_mode(&mut self, db: &DB) -> Result<()> {
        if self.mode == SwitchMode::Normal {
            return Ok(());
        }

        self.backup_db_options.set_options(db)?;
        for (cf_name, cf_opts) in &self.backup_cf_options {
            cf_opts.set_options(db, cf_name)?;
        }

        self.mode = SwitchMode::Normal;
        Ok(())
    }

    pub fn enter_import_mode(&mut self, db: &DB) -> Result<()> {
        if self.mode == SwitchMode::Import {
            return Ok(());
        }

        self.backup_db_options = ImportModeDBOptions::new_options(db);
        self.backup_cf_options.clear();

        let import_db_options = ImportModeDBOptions::new();
        import_db_options.set_options(db)?;
        let import_cf_options = ImportModeCFOptions::new();
        for cf_name in db.cf_names() {
            let cf_opts = ImportModeCFOptions::new_options(db, cf_name);
            self.backup_cf_options.push((cf_name.to_owned(), cf_opts));
            import_cf_options.set_options(db, cf_name)?;
        }

        self.mode = SwitchMode::Import;
        Ok(())
    }
}

struct ImportModeDBOptions {
    max_background_jobs: i32,
}

impl ImportModeDBOptions {
    fn new() -> ImportModeDBOptions {
        ImportModeDBOptions {
            max_background_jobs: 32,
        }
    }

    fn new_options(db: &DB) -> ImportModeDBOptions {
        let db_opts = db.get_db_options();
        ImportModeDBOptions {
            max_background_jobs: db_opts.get_max_background_jobs(),
        }
    }

    fn set_options(&self, db: &DB) -> Result<()> {
        let opts = [(
            "max_background_jobs".to_string(),
            self.max_background_jobs.to_string(),
        )];
        let tmp_opts: Vec<_> = opts.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
        db.set_db_options(&tmp_opts)?;
        Ok(())
    }
}

struct ImportModeCFOptions {
    block_cache_size: u64,
    level0_stop_writes_trigger: u32,
    level0_slowdown_writes_trigger: u32,
    soft_pending_compaction_bytes_limit: u64,
    hard_pending_compaction_bytes_limit: u64,
}

impl ImportModeCFOptions {
    fn new() -> ImportModeCFOptions {
        ImportModeCFOptions {
            block_cache_size: 4 << 30,
            level0_stop_writes_trigger: 1 << 30,
            level0_slowdown_writes_trigger: 1 << 30,
            soft_pending_compaction_bytes_limit: 0,
            hard_pending_compaction_bytes_limit: 0,
        }
    }

    fn new_options(db: &DB, cf_name: &str) -> ImportModeCFOptions {
        let cf = db.cf_handle(cf_name).unwrap();
        let cf_opts = db.get_options_cf(cf);

        ImportModeCFOptions {
            block_cache_size: cf_opts.get_block_cache_capacity(),
            level0_stop_writes_trigger: cf_opts.get_level_zero_stop_writes_trigger(),
            level0_slowdown_writes_trigger: cf_opts.get_level_zero_slowdown_writes_trigger(),
            soft_pending_compaction_bytes_limit: cf_opts.get_soft_pending_compaction_bytes_limit(),
            hard_pending_compaction_bytes_limit: cf_opts.get_hard_pending_compaction_bytes_limit(),
        }
    }

    fn set_options(&self, db: &DB, cf_name: &str) -> Result<()> {
        let cf = db.cf_handle(cf_name).unwrap();
        let cf_opts = db.get_options_cf(cf);
        cf_opts.set_block_cache_capacity(self.block_cache_size)?;

        let opts = [
            (
                "level0_stop_writes_trigger".to_owned(),
                self.level0_stop_writes_trigger.to_string(),
            ),
            (
                "level0_slowdown_writes_trigger".to_owned(),
                self.level0_slowdown_writes_trigger.to_string(),
            ),
            (
                "soft_pending_compaction_bytes_limit".to_owned(),
                self.soft_pending_compaction_bytes_limit.to_string(),
            ),
            (
                "hard_pending_compaction_bytes_limit".to_owned(),
                self.hard_pending_compaction_bytes_limit.to_string(),
            ),
        ];

        let tmp_opts: Vec<_> = opts.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
        db.set_options_cf(cf, tmp_opts.as_slice())?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::util::rocksdb_util::new_engine;
    use tempdir::TempDir;

    fn check_import_options(
        db: &DB,
        expected_db_opts: &ImportModeDBOptions,
        expected_cf_opts: &ImportModeCFOptions,
    ) {
        let db_opts = db.get_db_options();
        assert_eq!(
            db_opts.get_max_background_jobs(),
            expected_db_opts.max_background_jobs,
        );

        for cf_name in db.cf_names() {
            let cf = db.cf_handle(cf_name).unwrap();
            let cf_opts = db.get_options_cf(cf);
            assert_eq!(
                cf_opts.get_block_cache_capacity(),
                expected_cf_opts.block_cache_size
            );
            assert_eq!(
                cf_opts.get_level_zero_stop_writes_trigger(),
                expected_cf_opts.level0_stop_writes_trigger
            );
            assert_eq!(
                cf_opts.get_level_zero_slowdown_writes_trigger(),
                expected_cf_opts.level0_slowdown_writes_trigger
            );
            assert_eq!(
                cf_opts.get_soft_pending_compaction_bytes_limit(),
                expected_cf_opts.soft_pending_compaction_bytes_limit
            );
            assert_eq!(
                cf_opts.get_hard_pending_compaction_bytes_limit(),
                expected_cf_opts.hard_pending_compaction_bytes_limit
            );
        }
    }

    #[test]
    fn test_import_mode_switcher() {
        let temp_dir = TempDir::new("test_import_mode_switcher").unwrap();
        let db = new_engine(temp_dir.path().to_str().unwrap(), &["a", "b"], None).unwrap();

        let import_db_options = ImportModeDBOptions::new();
        let normal_db_options = ImportModeDBOptions::new_options(&db);
        let import_cf_options = ImportModeCFOptions::new();
        let normal_cf_options = ImportModeCFOptions::new_options(&db, "default");

        let mut switcher = ImportModeSwitcher::new();
        check_import_options(&db, &normal_db_options, &normal_cf_options);
        switcher.enter_import_mode(&db).unwrap();
        check_import_options(&db, &import_db_options, &import_cf_options);
        switcher.enter_import_mode(&db).unwrap();
        check_import_options(&db, &import_db_options, &import_cf_options);
        switcher.enter_normal_mode(&db).unwrap();
        check_import_options(&db, &normal_db_options, &normal_cf_options);
        switcher.enter_normal_mode(&db).unwrap();
        check_import_options(&db, &normal_db_options, &normal_cf_options);
    }
}
<|endoftext|>