<commit_before>use std::sync::Mutex;
use stopwatch;
use time;

use common::protocol;
use common::surroundings_loader;
use common::surroundings_loader::LoadType;

use audio_thread;
use chunk;
use client;
use lod;
use server_update::apply_server_update;
use terrain;
use view;

const MAX_OUTSTANDING_TERRAIN_REQUESTS: u32 = 1;

pub fn update_thread<RecvServer, UpdateView0, UpdateView1, UpdateAudio, UpdateServer, EnqueueTerrainLoad>(
  quit                   : &Mutex<bool>,
  client                 : &client::T,
  recv_server            : &mut RecvServer,
  update_view0           : &mut UpdateView0,
  update_view1           : &mut UpdateView1,
  update_audio           : &mut UpdateAudio,
  update_server          : &mut UpdateServer,
  enqueue_terrain_update : &mut EnqueueTerrainLoad,
) where
  RecvServer           : FnMut() -> Option<protocol::ServerToClient>,
  UpdateView0          : FnMut(view::update::T),
  UpdateView1          : FnMut(view::update::T),
  UpdateAudio          : FnMut(audio_thread::Message),
  UpdateServer         : FnMut(protocol::ClientToServer),
  EnqueueTerrainLoad : FnMut(terrain::Load),
{
  'update_loop: loop {
    let should_quit = *quit.lock().unwrap();
    if should_quit {
      break 'update_loop
    } else {
      stopwatch::time("update_iteration", || {
        stopwatch::time("process_server_updates", || {
          process_server_updates(client, recv_server, update_view0, update_audio, update_server, enqueue_terrain_update);
        });

        stopwatch::time("update_surroundings", || {
          update_surroundings(client, update_view1, update_server);
        });

        stopwatch::time("process_voxel_updates", || {
          process_voxel_updates(client, update_view1);
        });
      })
    }
  }
}

#[inline(never)]
fn update_surroundings<UpdateView, UpdateServer>(
  client        : &client::T,
  update_view   : &mut UpdateView,
  update_server : &mut UpdateServer,
) where
  UpdateView   : FnMut(view::update::T),
  UpdateServer : FnMut(protocol::ClientToServer),
{
  let start = time::precise_time_ns();
  let mut i = 0;
  let load_position = {
    let load_position = *client.load_position.lock().unwrap();
    load_position.unwrap_or_else(|| *client.player_position.lock().unwrap())
  };
  let load_position = chunk::position::of_world_position(&load_position);
  let mut surroundings_loader = client.surroundings_loader.lock().unwrap();
  let mut updates = surroundings_loader.updates(load_position.as_pnt()) ;
  loop {
    if *client.pending_terrain_requests.lock().unwrap() >= MAX_OUTSTANDING_TERRAIN_REQUESTS {
      trace!("update loop breaking");
      break;
    }

    let chunk_position;
    let load_type;
    match updates.next() {
      None => break,
      Some((b, l)) => {
        chunk_position = chunk::position::of_pnt(&b);
        load_type = l;
      },
    }

    debug!("chunk surroundings");
    let distance =
      surroundings_loader::distance_between(
        load_position.as_pnt(),
        chunk_position.as_pnt(),
      );
    match load_type {
      LoadType::Load => {
        stopwatch::time("update_thread.load_chunk", || {
          trace!("Loading distance {}", distance);
          let new_lod = lod::of_distance(distance as u32);
          let load_state = client.terrain.lock().unwrap().load_state(&chunk_position);
          if load_state == Some(new_lod) {
            debug!("Not re-loading {:?} at {:?}", chunk_position, new_lod);
          } else {
            load_or_request_chunk(client, update_server, update_view, &chunk_position, new_lod);
          }
        })
      },
      LoadType::Downgrade => {
        stopwatch::time("update_thread.update_chunk", || {
          let new_lod = lod::of_distance(distance as u32);
          let load_state = client.terrain.lock().unwrap().load_state(&chunk_position);
          let is_downgrade = load_state.map(|lod| new_lod < lod) == Some(true);
          if is_downgrade {
            load_or_request_chunk(client, update_server, update_view, &chunk_position, new_lod);
          } else {
            trace!("Not updating {:?} at {:?}", chunk_position, new_lod);
          }
        })
      },
      LoadType::Unload => {
        stopwatch::time("update_thread.unload", || {
          client.terrain.lock().unwrap().unload(update_view, &chunk_position);
        })
      },
    };

    if i >= 10 {
      i -= 10;
      if time::precise_time_ns() - start >= 1_000_000 {
        break
      }
    }
    i += 1;
  }
}

fn load_or_request_chunk<UpdateServer, UpdateView>(
  client         : &client::T,
  update_server  : &mut UpdateServer,
  update_view    : &mut UpdateView,
  chunk_position : &chunk::position::T,
  lod            : lod::T,
) where
  UpdateServer: FnMut(protocol::ClientToServer),
  UpdateView: FnMut(view::update::T),
{
  let mut terrain = client.terrain.lock().unwrap();
  let rng = &mut *client.rng.lock().unwrap();
  let r =
    terrain.load_chunk(
      &client.id_allocator,
      &mut *rng,
      update_view,
      chunk_position,
      lod,
    );
  match r {
    Ok(()) => {},
    Err(voxels) => {
      update_server(
        protocol::ClientToServer::RequestVoxels {
          request_time_ns : time::precise_time_ns(),
          client_id       : client.id,
          voxels          : voxels,
        }
      );
      *client.pending_terrain_requests.lock().unwrap() += 1;
    },
  }
}

#[inline(never)]
fn process_voxel_updates<UpdateView>(
  client      : &client::T,
  update_view : &mut UpdateView,
) where
  UpdateView: FnMut(view::update::T),
{
  let terrain = &mut *client.terrain.lock().unwrap();
  let rng = &mut *client.rng.lock().unwrap();
  terrain.tick(
    &client.id_allocator,
    rng,
    update_view,
    &*client.player_position.lock().unwrap(),
  );
}

#[inline(never)]
fn process_server_updates<RecvServer, UpdateView, UpdateAudio, UpdateServer, EnqueueTerrainLoad>(
  client: &client::T,
  recv_server: &mut RecvServer,
  update_view: &mut UpdateView,
  update_audio: &mut UpdateAudio,
  update_server: &mut UpdateServer,
  enqueue_terrain_update: &mut EnqueueTerrainLoad,
) where
  RecvServer           : FnMut() -> Option<protocol::ServerToClient>,
  UpdateView           : FnMut(view::update::T),
  UpdateAudio          : FnMut(audio_thread::Message),
  UpdateServer         : FnMut(protocol::ClientToServer),
  EnqueueTerrainLoad : FnMut(terrain::Load),
{
  let start = time::precise_time_ns();
  let mut i = 0;
  while let Some(up) = recv_server() {
    apply_server_update(
      client,
      update_view,
      update_audio,
      update_server,
      enqueue_terrain_update,
      up,
    );

    if i > 10 {
      i -= 10;
      if time::precise_time_ns() - start >= 1_000_000 {
        break
      }
    }
    i += 1;
  }
}
<commit_msg>rename<commit_after>use std::sync::Mutex;
use stopwatch;
use time;

use common::protocol;
use common::surroundings_loader;
use common::surroundings_loader::LoadType;

use audio_thread;
use chunk;
use client;
use lod;
use server_update::apply_server_update;
use terrain;
use view;

const MAX_OUTSTANDING_TERRAIN_REQUESTS: u32 = 1;

pub fn update_thread<RecvServer, UpdateView0, UpdateView1, UpdateAudio, UpdateServer, EnqueueTerrainLoad>(
  quit                   : &Mutex<bool>,
  client                 : &client::T,
  recv_server            : &mut RecvServer,
  update_view0           : &mut UpdateView0,
  update_view1           : &mut UpdateView1,
  update_audio           : &mut UpdateAudio,
  update_server          : &mut UpdateServer,
  enqueue_terrain_load : &mut EnqueueTerrainLoad,
) where
  RecvServer           : FnMut() -> Option<protocol::ServerToClient>,
  UpdateView0          : FnMut(view::update::T),
  UpdateView1          : FnMut(view::update::T),
  UpdateAudio          : FnMut(audio_thread::Message),
  UpdateServer         : FnMut(protocol::ClientToServer),
  EnqueueTerrainLoad : FnMut(terrain::Load),
{
  'update_loop: loop {
    let should_quit = *quit.lock().unwrap();
    if should_quit {
      break 'update_loop
    } else {
      stopwatch::time("update_iteration", || {
        stopwatch::time("process_server_updates", || {
          process_server_updates(client, recv_server, update_view0, update_audio, update_server, enqueue_terrain_load);
        });

        stopwatch::time("update_surroundings", || {
          update_surroundings(client, update_view1, update_server);
        });

        stopwatch::time("process_voxel_updates", || {
          process_voxel_updates(client, update_view1);
        });
      })
    }
  }
}

#[inline(never)]
fn update_surroundings<UpdateView, UpdateServer>(
  client        : &client::T,
  update_view   : &mut UpdateView,
  update_server : &mut UpdateServer,
) where
  UpdateView   : FnMut(view::update::T),
  UpdateServer : FnMut(protocol::ClientToServer),
{
  let start = time::precise_time_ns();
  let mut i = 0;
  let load_position = {
    let load_position = *client.load_position.lock().unwrap();
    load_position.unwrap_or_else(|| *client.player_position.lock().unwrap())
  };
  let load_position = chunk::position::of_world_position(&load_position);
  let mut surroundings_loader = client.surroundings_loader.lock().unwrap();
  let mut updates = surroundings_loader.updates(load_position.as_pnt()) ;
  loop {
    if *client.pending_terrain_requests.lock().unwrap() >= MAX_OUTSTANDING_TERRAIN_REQUESTS {
      trace!("update loop breaking");
      break;
    }

    let chunk_position;
    let load_type;
    match updates.next() {
      None => break,
      Some((b, l)) => {
        chunk_position = chunk::position::of_pnt(&b);
        load_type = l;
      },
    }

    debug!("chunk surroundings");
    let distance =
      surroundings_loader::distance_between(
        load_position.as_pnt(),
        chunk_position.as_pnt(),
      );
    match load_type {
      LoadType::Load => {
        stopwatch::time("update_thread.load_chunk", || {
          trace!("Loading distance {}", distance);
          let new_lod = lod::of_distance(distance as u32);
          let load_state = client.terrain.lock().unwrap().load_state(&chunk_position);
          if load_state == Some(new_lod) {
            debug!("Not re-loading {:?} at {:?}", chunk_position, new_lod);
          } else {
            load_or_request_chunk(client, update_server, update_view, &chunk_position, new_lod);
          }
        })
      },
      LoadType::Downgrade => {
        stopwatch::time("update_thread.update_chunk", || {
          let new_lod = lod::of_distance(distance as u32);
          let load_state = client.terrain.lock().unwrap().load_state(&chunk_position);
          let is_downgrade = load_state.map(|lod| new_lod < lod) == Some(true);
          if is_downgrade {
            load_or_request_chunk(client, update_server, update_view, &chunk_position, new_lod);
          } else {
            trace!("Not updating {:?} at {:?}", chunk_position, new_lod);
          }
        })
      },
      LoadType::Unload => {
        stopwatch::time("update_thread.unload", || {
          client.terrain.lock().unwrap().unload(update_view, &chunk_position);
        })
      },
    };

    if i >= 10 {
      i -= 10;
      if time::precise_time_ns() - start >= 1_000_000 {
        break
      }
    }
    i += 1;
  }
}

fn load_or_request_chunk<UpdateServer, UpdateView>(
  client         : &client::T,
  update_server  : &mut UpdateServer,
  update_view    : &mut UpdateView,
  chunk_position : &chunk::position::T,
  lod            : lod::T,
) where
  UpdateServer: FnMut(protocol::ClientToServer),
  UpdateView: FnMut(view::update::T),
{
  let mut terrain = client.terrain.lock().unwrap();
  let rng = &mut *client.rng.lock().unwrap();
  let r =
    terrain.load_chunk(
      &client.id_allocator,
      &mut *rng,
      update_view,
      chunk_position,
      lod,
    );
  match r {
    Ok(()) => {},
    Err(voxels) => {
      update_server(
        protocol::ClientToServer::RequestVoxels {
          request_time_ns : time::precise_time_ns(),
          client_id       : client.id,
          voxels          : voxels,
        }
      );
      *client.pending_terrain_requests.lock().unwrap() += 1;
    },
  }
}

#[inline(never)]
fn process_voxel_updates<UpdateView>(
  client      : &client::T,
  update_view : &mut UpdateView,
) where
  UpdateView: FnMut(view::update::T),
{
  let terrain = &mut *client.terrain.lock().unwrap();
  let rng = &mut *client.rng.lock().unwrap();
  terrain.tick(
    &client.id_allocator,
    rng,
    update_view,
    &*client.player_position.lock().unwrap(),
  );
}

#[inline(never)]
fn process_server_updates<RecvServer, UpdateView, UpdateAudio, UpdateServer, EnqueueTerrainLoad>(
  client: &client::T,
  recv_server: &mut RecvServer,
  update_view: &mut UpdateView,
  update_audio: &mut UpdateAudio,
  update_server: &mut UpdateServer,
  enqueue_terrain_load: &mut EnqueueTerrainLoad,
) where
  RecvServer           : FnMut() -> Option<protocol::ServerToClient>,
  UpdateView           : FnMut(view::update::T),
  UpdateAudio          : FnMut(audio_thread::Message),
  UpdateServer         : FnMut(protocol::ClientToServer),
  EnqueueTerrainLoad : FnMut(terrain::Load),
{
  let start = time::precise_time_ns();
  let mut i = 0;
  while let Some(up) = recv_server() {
    apply_server_update(
      client,
      update_view,
      update_audio,
      update_server,
      enqueue_terrain_load,
      up,
    );

    if i > 10 {
      i -= 10;
      if time::precise_time_ns() - start >= 1_000_000 {
        break
      }
    }
    i += 1;
  }
}
<|endoftext|>


<commit_before>//! This module contains paths to types and functions Clippy needs to know
//! about.

pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
pub const ARC: [&str; 3] = ["alloc", "sync", "Arc"];
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
pub const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
pub const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
pub const BTREEMAP_ENTRY: [&str; 5] = ["alloc", "collections", "btree", "map", "Entry"];
pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
pub const CSTRING_NEW: [&str; 5] = ["std", "ffi", "c_str", "CString", "new"];
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
pub const FMT_ARGUMENTS_NEWV1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
pub const FMT_ARGUMENTS_NEWV1FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
pub const HASH: [&str; 2] = ["hash", "Hash"];
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
pub const INIT: [&str; 4] = ["core", "intrinsics", "", "init"];
pub const INTO: [&str; 3] = ["core", "convert", "Into"];
pub const INTO_ITERATOR: [&str; 4] = ["core", "iter", "traits", "IntoIterator"];
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
pub const LINT: [&str; 3] = ["rustc", "lint", "Lint"];
pub const LINT_PASS: [&str; 3] = ["rustc", "lint", "LintPass"];
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
pub const MEM_UNINIT: [&str; 3] = ["core", "mem", "uninitialized"];
pub const MEM_ZEROED: [&str; 3] = ["core", "mem", "zeroed"];
pub const MUTEX: [&str; 4] = ["std", "sync", "mutex", "Mutex"];
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
pub const OPTION: [&str; 3] = ["core", "option", "Option"];
pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
pub const PARTIAL_ORD: [&str; 3] = ["core", "cmp", "PartialOrd"];
pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
pub const RANGE: [&str; 3] = ["core", "ops", "Range"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"];
pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"];
pub const RANGE_FULL: [&str; 3] = ["core", "ops", "RangeFull"];
pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"];
pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"];
pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"];
pub const RANGE_STD: [&str; 3] = ["std", "ops", "Range"];
pub const RANGE_TO: [&str; 3] = ["core", "ops", "RangeTo"];
pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"];
pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"];
pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
pub const RESULT: [&str; 3] = ["core", "result", "Result"];
pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"];
pub const STRING: [&str; 3] = ["alloc", "string", "String"];
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
pub const UNINIT: [&str; 4] = ["core", "intrinsics", "", "uninit"];
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
<commit_msg>Update more changed iterator paths<commit_after>//! This module contains paths to types and functions Clippy needs to know
//! about.

pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
pub const ARC: [&str; 3] = ["alloc", "sync", "Arc"];
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
pub const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
pub const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
pub const BTREEMAP_ENTRY: [&str; 5] = ["alloc", "collections", "btree", "map", "Entry"];
pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
pub const CSTRING_NEW: [&str; 5] = ["std", "ffi", "c_str", "CString", "new"];
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
pub const FMT_ARGUMENTS_NEWV1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
pub const FMT_ARGUMENTS_NEWV1FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
pub const HASH: [&str; 2] = ["hash", "Hash"];
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
pub const INIT: [&str; 4] = ["core", "intrinsics", "", "init"];
pub const INTO: [&str; 3] = ["core", "convert", "Into"];
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
pub const LINT: [&str; 3] = ["rustc", "lint", "Lint"];
pub const LINT_PASS: [&str; 3] = ["rustc", "lint", "LintPass"];
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
pub const MEM_UNINIT: [&str; 3] = ["core", "mem", "uninitialized"];
pub const MEM_ZEROED: [&str; 3] = ["core", "mem", "zeroed"];
pub const MUTEX: [&str; 4] = ["std", "sync", "mutex", "Mutex"];
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
pub const OPTION: [&str; 3] = ["core", "option", "Option"];
pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
pub const PARTIAL_ORD: [&str; 3] = ["core", "cmp", "PartialOrd"];
pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
pub const RANGE: [&str; 3] = ["core", "ops", "Range"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"];
pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"];
pub const RANGE_FULL: [&str; 3] = ["core", "ops", "RangeFull"];
pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"];
pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"];
pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"];
pub const RANGE_STD: [&str; 3] = ["std", "ops", "Range"];
pub const RANGE_TO: [&str; 3] = ["core", "ops", "RangeTo"];
pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"];
pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"];
pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
pub const RESULT: [&str; 3] = ["core", "result", "Result"];
pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"];
pub const STRING: [&str; 3] = ["alloc", "string", "String"];
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
pub const UNINIT: [&str; 4] = ["core", "intrinsics", "", "uninit"];
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
<|endoftext|>


<commit_before>//! Handles dynamic library loading for proc macro

use crate::{proc_macro::bridge, rustc_server::TokenStream};
use std::fs::File;
use std::path::Path;

use goblin::{mach::Mach, Object};
use libloading::Library;
use memmap::Mmap;
use ra_proc_macro::ProcMacroKind;

use std::io;

const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";

fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
    io::Error::new(io::ErrorKind::InvalidData, e)
}

fn is_derive_registrar_symbol(symbol: &str) -> bool {
    symbol.contains(NEW_REGISTRAR_SYMBOL)
}

fn find_registrar_symbol(file: &Path) -> io::Result<Option<String>> {
    let file = File::open(file)?;
    let buffer = unsafe { Mmap::map(&file)? };
    let object = Object::parse(&buffer).map_err(invalid_data_err)?;

    let name = match object {
        Object::Elf(elf) => {
            let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
            symbols.into_iter().find(|s| is_derive_registrar_symbol(s)).map(&str::to_owned)
        }
        Object::PE(pe) => pe
            .exports
            .iter()
            .flat_map(|s| s.name)
            .find(|s| is_derive_registrar_symbol(s))
            .map(&str::to_owned),
        Object::Mach(Mach::Binary(binary)) => {
            let exports = binary.exports().map_err(invalid_data_err)?;
            exports
                .iter()
                .map(|s| {
                    // In macos doc:
                    // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
                    // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
                    // prepended with an underscore.
                    if s.name.starts_with("_") {
                        &s.name[1..]
                    } else {
                        &s.name
                    }
                })
                .find(|s| is_derive_registrar_symbol(s))
                .map(&str::to_owned)
        }
        _ => return Ok(None),
    };
    return Ok(name);
}

/// Loads dynamic library in platform dependent manner.
///
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
/// and [here](https://github.com/rust-lang/rust/issues/60593).
///
/// Usage of RTLD_DEEPBIND
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
///
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
#[cfg(windows)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    Library::new(file)
}

#[cfg(unix)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    use libloading::os::unix::Library as UnixLibrary;
    use std::os::raw::c_int;

    const RTLD_NOW: c_int = 0x00002;
    const RTLD_DEEPBIND: c_int = 0x00008;

    UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into())
}

struct ProcMacroLibraryLibloading {
    // Hold the dylib to prevent it from unloading
    _lib: Library,
    exported_macros: Vec<bridge::client::ProcMacro>,
}

impl ProcMacroLibraryLibloading {
    fn open(file: &Path) -> io::Result<Self> {
        let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
            invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display()))
        })?;

        let lib = load_library(file).map_err(invalid_data_err)?;
        let exported_macros = {
            let macros: libloading::Symbol<&&[bridge::client::ProcMacro]> =
                unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
            macros.to_vec()
        };

        Ok(ProcMacroLibraryLibloading { _lib: lib, exported_macros })
    }
}

type ProcMacroLibraryImpl = ProcMacroLibraryLibloading;

pub struct Expander {
    inner: ProcMacroLibraryImpl,
}

impl Expander {
    pub fn new(lib: &Path) -> Result<Expander, String> {
        // Some libraries for dynamic loading require canonicalized path even when it is
        // already absolute
        let lib = lib
            .canonicalize()
            .unwrap_or_else(|err| panic!("Cannot canonicalize {}: {:?}", lib.display(), err));

        let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;

        Ok(Expander { inner: library })
    }

    pub fn expand(
        &self,
        macro_name: &str,
        macro_body: &ra_tt::Subtree,
        attributes: Option<&ra_tt::Subtree>,
    ) -> Result<ra_tt::Subtree, bridge::PanicMessage> {
        let parsed_body = TokenStream::with_subtree(macro_body.clone());

        let parsed_attributes = attributes
            .map_or(crate::rustc_server::TokenStream::new(), |attr| {
                TokenStream::with_subtree(attr.clone())
            });

        for proc_macro in &self.inner.exported_macros {
            match proc_macro {
                bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
                    if *trait_name == macro_name =>
                {
                    let res = client.run(
                        &crate::proc_macro::bridge::server::SameThread,
                        crate::rustc_server::Rustc::default(),
                        parsed_body,
                    );
                    return res.map(|it| it.subtree);
                }
                bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
                    let res = client.run(
                        &crate::proc_macro::bridge::server::SameThread,
                        crate::rustc_server::Rustc::default(),
                        parsed_body,
                    );
                    return res.map(|it| it.subtree);
                }
                bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
                    let res = client.run(
                        &crate::proc_macro::bridge::server::SameThread,
                        crate::rustc_server::Rustc::default(),
                        parsed_attributes,
                        parsed_body,
                    );
                    return res.map(|it| it.subtree);
                }
                _ => continue,
            }
        }

        Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
    }

    pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
        self.inner
            .exported_macros
            .iter()
            .map(|proc_macro| match proc_macro {
                bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
                    (trait_name.to_string(), ProcMacroKind::CustomDerive)
                }
                bridge::client::ProcMacro::Bang { name, .. } => {
                    (name.to_string(), ProcMacroKind::FuncLike)
                }
                bridge::client::ProcMacro::Attr { name, .. } => {
                    (name.to_string(), ProcMacroKind::Attr)
                }
            })
            .collect()
    }
}
<commit_msg>Copy dylib to temp directory<commit_after>//! Handles dynamic library loading for proc macro

use crate::{proc_macro::bridge, rustc_server::TokenStream};
use std::fs::File;
use std::path::{Path, PathBuf};

use goblin::{mach::Mach, Object};
use libloading::Library;
use memmap::Mmap;
use ra_proc_macro::ProcMacroKind;

use std::io;

const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";

fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
    io::Error::new(io::ErrorKind::InvalidData, e)
}

fn is_derive_registrar_symbol(symbol: &str) -> bool {
    symbol.contains(NEW_REGISTRAR_SYMBOL)
}

fn find_registrar_symbol(file: &Path) -> io::Result<Option<String>> {
    let file = File::open(file)?;
    let buffer = unsafe { Mmap::map(&file)? };
    let object = Object::parse(&buffer).map_err(invalid_data_err)?;

    let name = match object {
        Object::Elf(elf) => {
            let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
            symbols.into_iter().find(|s| is_derive_registrar_symbol(s)).map(&str::to_owned)
        }
        Object::PE(pe) => pe
            .exports
            .iter()
            .flat_map(|s| s.name)
            .find(|s| is_derive_registrar_symbol(s))
            .map(&str::to_owned),
        Object::Mach(Mach::Binary(binary)) => {
            let exports = binary.exports().map_err(invalid_data_err)?;
            exports
                .iter()
                .map(|s| {
                    // In macos doc:
                    // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
                    // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
                    // prepended with an underscore.
                    if s.name.starts_with("_") {
                        &s.name[1..]
                    } else {
                        &s.name
                    }
                })
                .find(|s| is_derive_registrar_symbol(s))
                .map(&str::to_owned)
        }
        _ => return Ok(None),
    };
    return Ok(name);
}

/// Loads dynamic library in platform dependent manner.
///
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
/// and [here](https://github.com/rust-lang/rust/issues/60593).
///
/// Usage of RTLD_DEEPBIND
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
///
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
#[cfg(windows)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    Library::new(file)
}

#[cfg(unix)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    use libloading::os::unix::Library as UnixLibrary;
    use std::os::raw::c_int;

    const RTLD_NOW: c_int = 0x00002;
    const RTLD_DEEPBIND: c_int = 0x00008;

    UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into())
}

struct ProcMacroLibraryLibloading {
    // Hold the dylib to prevent it from unloading
    _lib: Library,
    exported_macros: Vec<bridge::client::ProcMacro>,
}

impl ProcMacroLibraryLibloading {
    fn open(file: &Path) -> io::Result<Self> {
        let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
            invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display()))
        })?;

        let lib = load_library(file).map_err(invalid_data_err)?;
        let exported_macros = {
            let macros: libloading::Symbol<&&[bridge::client::ProcMacro]> =
                unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
            macros.to_vec()
        };

        Ok(ProcMacroLibraryLibloading { _lib: lib, exported_macros })
    }
}

type ProcMacroLibraryImpl = ProcMacroLibraryLibloading;

pub struct Expander {
    inner: ProcMacroLibraryImpl,
}

impl Expander {
    pub fn new(lib: &Path) -> Result<Expander, String> {
        // Some libraries for dynamic loading require canonicalized path even when it is
        // already absolute
        let lib = lib
            .canonicalize()
            .unwrap_or_else(|err| panic!("Cannot canonicalize {}: {:?}", lib.display(), err));

        // Copy the dylib to temp directory to prevent locking in Windows
        let lib = copy_to_temp_dir(&lib).map_err(|e| e.to_string())?;

        let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;

        Ok(Expander { inner: library })
    }

    pub fn expand(
        &self,
        macro_name: &str,
        macro_body: &ra_tt::Subtree,
        attributes: Option<&ra_tt::Subtree>,
    ) -> Result<ra_tt::Subtree, bridge::PanicMessage> {
        let parsed_body = TokenStream::with_subtree(macro_body.clone());

        let parsed_attributes = attributes
            .map_or(crate::rustc_server::TokenStream::new(), |attr| {
                TokenStream::with_subtree(attr.clone())
            });

        for proc_macro in &self.inner.exported_macros {
            match proc_macro {
                bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
                    if *trait_name == macro_name =>
                {
                    let res = client.run(
                        &crate::proc_macro::bridge::server::SameThread,
                        crate::rustc_server::Rustc::default(),
                        parsed_body,
                    );
                    return res.map(|it| it.subtree);
                }
                bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
                    let res = client.run(
                        &crate::proc_macro::bridge::server::SameThread,
                        crate::rustc_server::Rustc::default(),
                        parsed_body,
                    );
                    return res.map(|it| it.subtree);
                }
                bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
                    let res = client.run(
                        &crate::proc_macro::bridge::server::SameThread,
                        crate::rustc_server::Rustc::default(),
                        parsed_attributes,
                        parsed_body,
                    );
                    return res.map(|it| it.subtree);
                }
                _ => continue,
            }
        }

        Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
    }

    pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
        self.inner
            .exported_macros
            .iter()
            .map(|proc_macro| match proc_macro {
                bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
                    (trait_name.to_string(), ProcMacroKind::CustomDerive)
                }
                bridge::client::ProcMacro::Bang { name, .. } => {
                    (name.to_string(), ProcMacroKind::FuncLike)
                }
                bridge::client::ProcMacro::Attr { name, .. } => {
                    (name.to_string(), ProcMacroKind::Attr)
                }
            })
            .collect()
    }
}

fn copy_to_temp_dir(path: &Path) -> io::Result<PathBuf> {
    let mut to = std::env::temp_dir();
    let file_name = path.file_name().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidInput,
            format!("File path is invalid: {}", path.display()),
        )
    })?;

    to.push(file_name);
    std::fs::copy(path, &to)?;
    Ok(to)
}
<|endoftext|>


<commit_before>use crate::{
    SyntaxKind::{self, *},
    TextUnit,
};

/// A token of Rust source.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Token {
    /// The kind of token.
    pub kind: SyntaxKind,
    /// The length of the token.
    pub len: TextUnit,
}

macro_rules! match_literal_kind {
    ($kind:expr) => {
        match $kind {
            ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER,
            ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER,
            ra_rustc_lexer::LiteralKind::Char { .. } => CHAR,
            ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE,
            ra_rustc_lexer::LiteralKind::Str { .. } => STRING,
            ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING,
            ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING,
            ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING,
        }
    };
}

/// Break a string up into its component tokens
pub fn tokenize(text: &str) -> Vec<Token> {
    if text.is_empty() {
        return vec![];
    }
    let mut text = text;
    let mut acc = Vec::new();
    if let Some(len) = ra_rustc_lexer::strip_shebang(text) {
        acc.push(Token { kind: SHEBANG, len: TextUnit::from_usize(len) });
        text = &text[len..];
    }
    while !text.is_empty() {
        let rustc_token = ra_rustc_lexer::first_token(text);
        macro_rules! decompose {
            ($t1:expr, $t2:expr) => {{
                acc.push(Token { kind: $t1, len: 1.into() });
                acc.push(Token { kind: $t2, len: 1.into() });
                text = &text[2..];
                continue;
            }};
            ($t1:expr, $t2:expr, $t3:expr) => {{
                acc.push(Token { kind: $t1, len: 1.into() });
                acc.push(Token { kind: $t2, len: 1.into() });
                acc.push(Token { kind: $t3, len: 1.into() });
                text = &text[3..];
                continue;
            }};
        }
        let kind = match rustc_token.kind {
            ra_rustc_lexer::TokenKind::LineComment => COMMENT,
            ra_rustc_lexer::TokenKind::BlockComment { .. } => COMMENT,
            ra_rustc_lexer::TokenKind::Whitespace => WHITESPACE,
            ra_rustc_lexer::TokenKind::Ident => {
                let token_text = &text[..rustc_token.len];
                if token_text == "_" {
                    UNDERSCORE
                } else {
                    SyntaxKind::from_keyword(&text[..rustc_token.len]).unwrap_or(IDENT)
                }
            }
            ra_rustc_lexer::TokenKind::RawIdent => IDENT,
            ra_rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind!(kind),
            ra_rustc_lexer::TokenKind::Lifetime { .. } => LIFETIME,
            ra_rustc_lexer::TokenKind::Semi => SEMI,
            ra_rustc_lexer::TokenKind::Comma => COMMA,
            ra_rustc_lexer::TokenKind::DotDotDot => decompose!(DOT, DOT, DOT),
            ra_rustc_lexer::TokenKind::DotDotEq => decompose!(DOT, DOT, EQ),
            ra_rustc_lexer::TokenKind::DotDot => decompose!(DOT, DOT),
            ra_rustc_lexer::TokenKind::Dot => DOT,
            ra_rustc_lexer::TokenKind::OpenParen => L_PAREN,
            ra_rustc_lexer::TokenKind::CloseParen => R_PAREN,
            ra_rustc_lexer::TokenKind::OpenBrace => L_CURLY,
            ra_rustc_lexer::TokenKind::CloseBrace => R_CURLY,
            ra_rustc_lexer::TokenKind::OpenBracket => L_BRACK,
            ra_rustc_lexer::TokenKind::CloseBracket => R_BRACK,
            ra_rustc_lexer::TokenKind::At => AT,
            ra_rustc_lexer::TokenKind::Pound => POUND,
            ra_rustc_lexer::TokenKind::Tilde => TILDE,
            ra_rustc_lexer::TokenKind::Question => QUESTION,
            ra_rustc_lexer::TokenKind::ColonColon => decompose!(COLON, COLON),
            ra_rustc_lexer::TokenKind::Colon => COLON,
            ra_rustc_lexer::TokenKind::Dollar => DOLLAR,
            ra_rustc_lexer::TokenKind::EqEq => decompose!(EQ, EQ),
            ra_rustc_lexer::TokenKind::Eq => EQ,
            ra_rustc_lexer::TokenKind::FatArrow => decompose!(EQ, R_ANGLE),
            ra_rustc_lexer::TokenKind::Ne => decompose!(EXCL, EQ),
            ra_rustc_lexer::TokenKind::Not => EXCL,
            ra_rustc_lexer::TokenKind::Le => decompose!(L_ANGLE, EQ),
            ra_rustc_lexer::TokenKind::LArrow => decompose!(COLON, MINUS),
            ra_rustc_lexer::TokenKind::Lt => L_ANGLE,
            ra_rustc_lexer::TokenKind::ShlEq => decompose!(L_ANGLE, L_ANGLE, EQ),
            ra_rustc_lexer::TokenKind::Shl => decompose!(L_ANGLE, L_ANGLE),
            ra_rustc_lexer::TokenKind::Ge => decompose!(R_ANGLE, EQ),
            ra_rustc_lexer::TokenKind::Gt => R_ANGLE,
            ra_rustc_lexer::TokenKind::ShrEq => decompose!(R_ANGLE, R_ANGLE, EQ),
            ra_rustc_lexer::TokenKind::Shr => decompose!(R_ANGLE, R_ANGLE),
            ra_rustc_lexer::TokenKind::RArrow => decompose!(MINUS, R_ANGLE),
            ra_rustc_lexer::TokenKind::Minus => MINUS,
            ra_rustc_lexer::TokenKind::MinusEq => decompose!(MINUS, EQ),
            ra_rustc_lexer::TokenKind::And => AMP,
            ra_rustc_lexer::TokenKind::AndAnd => decompose!(AMP, AMP),
            ra_rustc_lexer::TokenKind::AndEq => decompose!(AMP, EQ),
            ra_rustc_lexer::TokenKind::Or => PIPE,
            ra_rustc_lexer::TokenKind::OrOr => decompose!(PIPE, PIPE),
            ra_rustc_lexer::TokenKind::OrEq => decompose!(PIPE, EQ),
            ra_rustc_lexer::TokenKind::PlusEq => decompose!(PLUS, EQ),
            ra_rustc_lexer::TokenKind::Plus => PLUS,
            ra_rustc_lexer::TokenKind::StarEq => decompose!(STAR, EQ),
            ra_rustc_lexer::TokenKind::Star => STAR,
            ra_rustc_lexer::TokenKind::SlashEq => decompose!(SLASH, EQ),
            ra_rustc_lexer::TokenKind::Slash => SLASH,
            ra_rustc_lexer::TokenKind::CaretEq => decompose!(CARET, EQ),
            ra_rustc_lexer::TokenKind::Caret => CARET,
            ra_rustc_lexer::TokenKind::PercentEq => decompose!(PERCENT, EQ),
            ra_rustc_lexer::TokenKind::Percent => PERCENT,
            ra_rustc_lexer::TokenKind::Unknown => ERROR,
        };
        let token = Token { kind, len: TextUnit::from_usize(rustc_token.len) };
        acc.push(token);
        text = &text[rustc_token.len..];
    }
    acc
}

pub fn classify_literal(text: &str) -> Option<Token> {
    let t = ra_rustc_lexer::first_token(text);
    if t.len != text.len() {
        return None;
    }
    let kind = match t.kind {
        ra_rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind!(kind),
        _ => return None,
    };
    Some(Token { kind, len: TextUnit::from_usize(t.len) })
}
<commit_msg>Change macro to function<commit_after>use crate::{
    SyntaxKind::{self, *},
    TextUnit,
};

/// A token of Rust source.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Token {
    /// The kind of token.
    pub kind: SyntaxKind,
    /// The length of the token.
    pub len: TextUnit,
}

fn match_literal_kind(kind: ra_rustc_lexer::LiteralKind) -> SyntaxKind {
    match kind {
        ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER,
        ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER,
        ra_rustc_lexer::LiteralKind::Char { .. } => CHAR,
        ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE,
        ra_rustc_lexer::LiteralKind::Str { .. } => STRING,
        ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING,
        ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING,
        ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING,
    }
}

/// Break a string up into its component tokens
pub fn tokenize(text: &str) -> Vec<Token> {
    if text.is_empty() {
        return vec![];
    }
    let mut text = text;
    let mut acc = Vec::new();
    if let Some(len) = ra_rustc_lexer::strip_shebang(text) {
        acc.push(Token { kind: SHEBANG, len: TextUnit::from_usize(len) });
        text = &text[len..];
    }
    while !text.is_empty() {
        let rustc_token = ra_rustc_lexer::first_token(text);
        macro_rules! decompose {
            ($t1:expr, $t2:expr) => {{
                acc.push(Token { kind: $t1, len: 1.into() });
                acc.push(Token { kind: $t2, len: 1.into() });
                text = &text[2..];
                continue;
            }};
            ($t1:expr, $t2:expr, $t3:expr) => {{
                acc.push(Token { kind: $t1, len: 1.into() });
                acc.push(Token { kind: $t2, len: 1.into() });
                acc.push(Token { kind: $t3, len: 1.into() });
                text = &text[3..];
                continue;
            }};
        }
        let kind = match rustc_token.kind {
            ra_rustc_lexer::TokenKind::LineComment => COMMENT,
            ra_rustc_lexer::TokenKind::BlockComment { .. } => COMMENT,
            ra_rustc_lexer::TokenKind::Whitespace => WHITESPACE,
            ra_rustc_lexer::TokenKind::Ident => {
                let token_text = &text[..rustc_token.len];
                if token_text == "_" {
                    UNDERSCORE
                } else {
                    SyntaxKind::from_keyword(&text[..rustc_token.len]).unwrap_or(IDENT)
                }
            }
            ra_rustc_lexer::TokenKind::RawIdent => IDENT,
            ra_rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind(kind),
            ra_rustc_lexer::TokenKind::Lifetime { .. } => LIFETIME,
            ra_rustc_lexer::TokenKind::Semi => SEMI,
            ra_rustc_lexer::TokenKind::Comma => COMMA,
            ra_rustc_lexer::TokenKind::DotDotDot => decompose!(DOT, DOT, DOT),
            ra_rustc_lexer::TokenKind::DotDotEq => decompose!(DOT, DOT, EQ),
            ra_rustc_lexer::TokenKind::DotDot => decompose!(DOT, DOT),
            ra_rustc_lexer::TokenKind::Dot => DOT,
            ra_rustc_lexer::TokenKind::OpenParen => L_PAREN,
            ra_rustc_lexer::TokenKind::CloseParen => R_PAREN,
            ra_rustc_lexer::TokenKind::OpenBrace => L_CURLY,
            ra_rustc_lexer::TokenKind::CloseBrace => R_CURLY,
            ra_rustc_lexer::TokenKind::OpenBracket => L_BRACK,
            ra_rustc_lexer::TokenKind::CloseBracket => R_BRACK,
            ra_rustc_lexer::TokenKind::At => AT,
            ra_rustc_lexer::TokenKind::Pound => POUND,
            ra_rustc_lexer::TokenKind::Tilde => TILDE,
            ra_rustc_lexer::TokenKind::Question => QUESTION,
            ra_rustc_lexer::TokenKind::ColonColon => decompose!(COLON, COLON),
            ra_rustc_lexer::TokenKind::Colon => COLON,
            ra_rustc_lexer::TokenKind::Dollar => DOLLAR,
            ra_rustc_lexer::TokenKind::EqEq => decompose!(EQ, EQ),
            ra_rustc_lexer::TokenKind::Eq => EQ,
            ra_rustc_lexer::TokenKind::FatArrow => decompose!(EQ, R_ANGLE),
            ra_rustc_lexer::TokenKind::Ne => decompose!(EXCL, EQ),
            ra_rustc_lexer::TokenKind::Not => EXCL,
            ra_rustc_lexer::TokenKind::Le => decompose!(L_ANGLE, EQ),
            ra_rustc_lexer::TokenKind::LArrow => decompose!(COLON, MINUS),
            ra_rustc_lexer::TokenKind::Lt => L_ANGLE,
            ra_rustc_lexer::TokenKind::ShlEq => decompose!(L_ANGLE, L_ANGLE, EQ),
            ra_rustc_lexer::TokenKind::Shl => decompose!(L_ANGLE, L_ANGLE),
            ra_rustc_lexer::TokenKind::Ge => decompose!(R_ANGLE, EQ),
            ra_rustc_lexer::TokenKind::Gt => R_ANGLE,
            ra_rustc_lexer::TokenKind::ShrEq => decompose!(R_ANGLE, R_ANGLE, EQ),
            ra_rustc_lexer::TokenKind::Shr => decompose!(R_ANGLE, R_ANGLE),
            ra_rustc_lexer::TokenKind::RArrow => decompose!(MINUS, R_ANGLE),
            ra_rustc_lexer::TokenKind::Minus => MINUS,
            ra_rustc_lexer::TokenKind::MinusEq => decompose!(MINUS, EQ),
            ra_rustc_lexer::TokenKind::And => AMP,
            ra_rustc_lexer::TokenKind::AndAnd => decompose!(AMP, AMP),
            ra_rustc_lexer::TokenKind::AndEq => decompose!(AMP, EQ),
            ra_rustc_lexer::TokenKind::Or => PIPE,
            ra_rustc_lexer::TokenKind::OrOr => decompose!(PIPE, PIPE),
            ra_rustc_lexer::TokenKind::OrEq => decompose!(PIPE, EQ),
            ra_rustc_lexer::TokenKind::PlusEq => decompose!(PLUS, EQ),
            ra_rustc_lexer::TokenKind::Plus => PLUS,
            ra_rustc_lexer::TokenKind::StarEq => decompose!(STAR, EQ),
            ra_rustc_lexer::TokenKind::Star => STAR,
            ra_rustc_lexer::TokenKind::SlashEq => decompose!(SLASH, EQ),
            ra_rustc_lexer::TokenKind::Slash => SLASH,
            ra_rustc_lexer::TokenKind::CaretEq => decompose!(CARET, EQ),
            ra_rustc_lexer::TokenKind::Caret => CARET,
            ra_rustc_lexer::TokenKind::PercentEq => decompose!(PERCENT, EQ),
            ra_rustc_lexer::TokenKind::Percent => PERCENT,
            ra_rustc_lexer::TokenKind::Unknown => ERROR,
        };
        let token = Token { kind, len: TextUnit::from_usize(rustc_token.len) };
        acc.push(token);
        text = &text[rustc_token.len..];
    }
    acc
}

pub fn classify_literal(text: &str) -> Option<Token> {
    let t = ra_rustc_lexer::first_token(text);
    if t.len != text.len() {
        return None;
    }
    let kind = match t.kind {
        ra_rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind(kind),
        _ => return None,
    };
    Some(Token { kind, len: TextUnit::from_usize(t.len) })
}
<|endoftext|>


<commit_before>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! (De)serializable types for the [Matrix Server-Server API][federation-api].
//! These types are used by server code.
//!
//! [federation-api]: https://matrix.org/docs/spec/server_server/r0.1.4.html

#![warn(missing_docs)]

mod serde;

pub mod authorization;
pub mod backfill;
pub mod device;
pub mod directory;
pub mod discovery;
pub mod event;
pub mod keys;
pub mod membership;
pub mod openid;
pub mod query;
pub mod thirdparty;
pub mod transactions;
<commit_msg>federation-api: Enable doc_cfg feature under cfg(docsrs)<commit_after>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! (De)serializable types for the [Matrix Server-Server API][federation-api].
//! These types are used by server code.
//!
//! [federation-api]: https://matrix.org/docs/spec/server_server/r0.1.4.html

#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

mod serde;

pub mod authorization;
pub mod backfill;
pub mod device;
pub mod directory;
pub mod discovery;
pub mod event;
pub mod keys;
pub mod membership;
pub mod openid;
pub mod query;
pub mod thirdparty;
pub mod transactions;
<|endoftext|>


<commit_before>fn filter_sequence(l: Vec<u32>) -> Vec<u32> {
    vec![]
}

fn main() {}
<commit_msg>a bit<commit_after>fn filter_sequence(l: Vec<u32>) -> Vec<u32> {
    let length = l.len();
    let mut re: Vec<u32> = vec![];
    for i in (1..length - 1).step_by(2) {
        re.push(l[i]);
    }
    re
}

fn main() {
    println!("{:?}", filter_sequence(vec![1, 2, 3, 4, 5, 6, 7]));
}
<|endoftext|>


<commit_before>use super::{
    colors::Colors, directory_stack::DirectoryStack, plugins::namespaces::{self, StringNamespace},
    status::{FAILURE, SUCCESS},
};
use fnv::FnvHashMap;
use liner::Context;
use std::{
    env, io::{self, BufRead},
};
use sys::{self, geteuid, getpid, getuid, is_root, variables as self_sys};
use types::{
    Array, ArrayVariableContext, HashMap, HashMapVariableContext, Identifier, Key, Value,
    VariableContext,
};
use unicode_segmentation::UnicodeSegmentation;
use xdg::BaseDirectories;

lazy_static! {
    static ref STRING_NAMESPACES: FnvHashMap<Identifier, StringNamespace> = namespaces::collect();
}

#[derive(Clone, Debug)]
pub struct Variables {
    pub hashmaps:  HashMapVariableContext,
    pub arrays:    ArrayVariableContext,
    pub variables: VariableContext,
    pub aliases:   VariableContext,
    flags:         u8,
}

impl Default for Variables {
    fn default() -> Variables {
        let mut map = FnvHashMap::with_capacity_and_hasher(64, Default::default());
        map.insert("DIRECTORY_STACK_SIZE".into(), "1000".into());
        map.insert("HISTORY_SIZE".into(), "1000".into());
        map.insert("HISTFILE_SIZE".into(), "100000".into());
        map.insert(
            "PROMPT".into(),
            "${x::1B}]0;${USER}: \
             ${PWD}${x::07}${c::0x55,bold}${USER}${c::default}:${c::0x4B}${SWD}${c::default}# \
             ${c::reset}"
                .into(),
        );

        // Set the PID, UID, and EUID variables.
        map.insert(
            "PID".into(),
            getpid().ok().map_or("?".into(), |id| id.to_string()),
        );
        map.insert(
            "UID".into(),
            getuid().ok().map_or("?".into(), |id| id.to_string()),
        );
        map.insert(
            "EUID".into(),
            geteuid().ok().map_or("?".into(), |id| id.to_string()),
        );

        // Initialize the HISTFILE variable
        if let Ok(base_dirs) = BaseDirectories::with_prefix("ion") {
            if let Ok(path) = base_dirs.place_data_file("history") {
                map.insert("HISTFILE".into(), path.to_str().unwrap_or("?").into());
                map.insert("HISTFILE_ENABLED".into(), "1".into());
            }
        }

        // Initialize the PWD (Present Working Directory) variable
        env::current_dir().ok().map_or_else(
            || env::set_var("PWD", "?"),
            |path| env::set_var("PWD", path.to_str().unwrap_or("?")),
        );

        // Initialize the HOME variable
        env::home_dir().map_or_else(
            || env::set_var("HOME", "?"),
            |path| env::set_var("HOME", path.to_str().unwrap_or("?")),
        );
        Variables {
            hashmaps:  FnvHashMap::with_capacity_and_hasher(64, Default::default()),
            arrays:    FnvHashMap::with_capacity_and_hasher(64, Default::default()),
            variables: map,
            aliases:   FnvHashMap::with_capacity_and_hasher(64, Default::default()),
            flags:     0,
        }
    }
}

const PLUGIN: u8 = 1;

impl Variables {
    #[allow(dead_code)]
    pub(crate) fn is_hashmap_reference(key: &str) -> Option<(Identifier, Key)> {
        let mut key_iter = key.split('[');

        if let Some(map_name) = key_iter.next() {
            if Variables::is_valid_variable_name(map_name) {
                if let Some(mut inner_key) = key_iter.next() {
                    if inner_key.ends_with(']') {
                        inner_key = inner_key.split(']').next().unwrap_or("");
                        inner_key = inner_key.trim_matches(|c| c == '\'' || c == '\"');
                        return Some((map_name.into(), inner_key.into()));
                    }
                }
            }
        }
        None
    }

    pub(crate) fn tilde_expansion(&self, word: &str, dir_stack: &DirectoryStack) -> Option<String> {
        let mut chars = word.char_indices();

        let tilde_prefix;
        let remainder;

        loop {
            if let Some((ind, c)) = chars.next() {
                if c == '/' || c == '$' {
                    tilde_prefix = &word[1..ind];
                    remainder = &word[ind..];
                    break;
                }
            } else {
                tilde_prefix = &word[1..];
                remainder = "";
                break;
            }
        }

        match tilde_prefix {
            "" => if let Some(home) = env::home_dir() {
                return Some(home.to_string_lossy().to_string() + remainder);
            },
            "+" => if let Some(pwd) = self.get_var("PWD") {
                return Some(pwd.to_string() + remainder);
            } else if let Ok(pwd) = env::current_dir() {
                return Some(pwd.to_string_lossy().to_string() + remainder);
            },
            "-" => if let Some(oldpwd) = self.get_var("OLDPWD") {
                return Some(oldpwd.to_string() + remainder);
            },
            _ => {
                let neg;
                let tilde_num;

                if tilde_prefix.starts_with('+') {
                    tilde_num = &tilde_prefix[1..];
                    neg = false;
                } else if tilde_prefix.starts_with('-') {
                    tilde_num = &tilde_prefix[1..];
                    neg = true;
                } else {
                    tilde_num = tilde_prefix;
                    neg = false;
                }

                match tilde_num.parse() {
                    Ok(num) => {
                        let res = if neg {
                            dir_stack.dir_from_top(num)
                        } else {
                            dir_stack.dir_from_bottom(num)
                        };

                        if let Some(path) = res {
                            return Some(path.to_str().unwrap().to_string());
                        }
                    }
                    Err(_) => if let Some(home) = self_sys::get_user_home(tilde_prefix) {
                        return Some(home + remainder);
                    },
                }
            }
        }
        None
    }

    pub(crate) fn is_valid_variable_name(name: &str) -> bool {
        name.chars().all(Variables::is_valid_variable_character)
    }

    pub(crate) fn is_valid_variable_character(c: char) -> bool {
        c.is_alphanumeric() || c == '_' || c == '?'
    }

    pub fn strings<'a>(&'a self) -> impl Iterator<Item = Identifier> + 'a {
        self.variables
            .keys()
            .cloned()
            .chain(env::vars().map(|(k, _)| k.into()))
    }

    pub fn unset_var(&mut self, name: &str) -> Option<Value> { self.variables.remove(name) }

    pub fn get_var_or_empty(&self, name: &str) -> Value { self.get_var(name).unwrap_or_default() }

    pub fn get_var(&self, name: &str) -> Option<Value> {
        match name {
            "MWD" => return Some(self.get_minimal_directory()),
            "SWD" => return Some(self.get_simplified_directory()),
            _ => (),
        }
        if let Some((name, variable)) = name.find("::").map(|pos| (&name[..pos], &name[pos + 2..]))
        {
            // If the parsed name contains the '::' pattern, then a namespace was
            // designated. Find it.
            match name {
                "c" | "color" => Colors::collect(variable).into_string(),
                "x" | "hex" => match u8::from_str_radix(variable, 16) {
                    Ok(c) => Some((c as char).to_string()),
                    Err(why) => {
                        eprintln!("ion: hex parse error: {}: {}", variable, why);
                        None
                    }
                },
                "env" => env::var(variable).map(Into::into).ok(),
                _ => {
                    if is_root() {
                        eprintln!("ion: root is not allowed to execute plugins");
                        return None;
                    }

                    if !self.has_plugin_support() {
                        eprintln!(
                            "ion: plugins are disabled. Considering enabling them with `let \
                             NS_PLUGINS = 1`"
                        );
                        return None;
                    }

                    // Attempt to obtain the given namespace from our lazily-generated map of
                    // namespaces.
                    if let Some(namespace) = STRING_NAMESPACES.get(name.into()) {
                        // Attempt to execute the given function from that namespace, and map it's
                        // results.
                        match namespace.execute(variable.into()) {
                            Ok(value) => value.map(Into::into),
                            Err(why) => {
                                eprintln!("ion: string namespace error: {}: {}", name, why);
                                None
                            }
                        }
                    } else {
                        eprintln!("ion: unsupported namespace: '{}'", name);
                        None
                    }
                }
            }
        } else {
            // Otherwise, it's just a simple variable name.
            self.variables
                .get(name)
                .cloned()
                .or_else(|| env::var(name).map(Into::into).ok())
        }
    }

    /// Obtains the value for the **MWD** variable.
    ///
    /// Further minimizes the directory path in the same manner that Fish does by default.
    /// That is, if more than two parents are visible in the path, all parent directories
    /// of the current directory will be reduced to a single character.
    fn get_minimal_directory(&self) -> Value {
        let swd = self.get_simplified_directory();

        {
            // Temporarily borrow the `swd` variable while we attempt to assemble a minimal
            // variant of the directory path. If that is not possible, we will cancel the
            // borrow and return `swd` itself as the minified path.
            let elements = swd
                .split("/")
                .filter(|s| !s.is_empty())
                .collect::<Vec<&str>>();
            if elements.len() > 2 {
                let mut output = String::new();
                for element in &elements[0..elements.len() - 1] {
                    let mut segmenter = UnicodeSegmentation::graphemes(*element, true);
                    let grapheme = segmenter.next().unwrap();
                    output.push_str(grapheme);
                    if grapheme == "." {
                        output.push_str(segmenter.next().unwrap());
                    }
                    output.push('/');
                }
                output.push_str(&elements[elements.len() - 1]);
                return output;
            }
        }

        swd
    }

    /// Obtains the value for the **SWD** variable.
    ///
    /// Useful for getting smaller prompts, this will produce a simplified variant of the
    /// working directory which the leading `HOME` prefix replaced with a tilde character.
    fn get_simplified_directory(&self) -> Value {
        self.get_var("PWD")
            .unwrap()
            .replace(&self.get_var("HOME").unwrap(), "~")
    }

    pub fn unset_array(&mut self, name: &str) -> Option<Array> { self.arrays.remove(name) }

    pub fn get_array(&self, name: &str) -> Option<&Array> { self.arrays.get(name) }

    pub fn get_map(&self, name: &str) -> Option<&HashMap> { self.hashmaps.get(name) }

    #[allow(dead_code)]
    pub(crate) fn set_hashmap_value(&mut self, name: &str, key: &str, value: &str) {
        if !name.is_empty() {
            if let Some(map) = self.hashmaps.get_mut(name) {
                map.insert(key.into(), value.into());
                return;
            }

            let mut map = HashMap::with_capacity_and_hasher(4, Default::default());
            map.insert(key.into(), value.into());
            self.hashmaps.insert(name.into(), map);
        }
    }

    pub fn set_array(&mut self, name: &str, value: Array) {
        if !name.is_empty() {
            if value.is_empty() {
                self.arrays.remove(name);
            } else {
                self.arrays.insert(name.into(), value);
            }
        }
    }

    pub fn set_var(&mut self, name: &str, value: &str) {
        if !name.is_empty() {
            if value.is_empty() {
                self.variables.remove(name);
            } else {
                if name == "NS_PLUGINS" {
                    match value {
                        "0" => self.disable_plugins(),
                        "1" => self.enable_plugins(),
                        _ => eprintln!(
                            "ion: unsupported value for NS_PLUGINS. Value must be either 0 or 1."
                        ),
                    }
                    return;
                }
                self.variables.insert(name.into(), value.into());
            }
        }
    }

    pub(crate) fn read<I: IntoIterator>(&mut self, args: I) -> i32
    where
        I::Item: AsRef<str>,
    {
        if sys::isatty(sys::STDIN_FILENO) {
            let mut con = Context::new();
            for arg in args.into_iter().skip(1) {
                match con.read_line(format!("{}=", arg.as_ref().trim()), &mut |_| {}) {
                    Ok(buffer) => self.set_var(arg.as_ref(), buffer.trim()),
                    Err(_) => return FAILURE,
                }
            }
        } else {
            let stdin = io::stdin();
            let handle = stdin.lock();
            let mut lines = handle.lines();
            for arg in args.into_iter().skip(1) {
                if let Some(Ok(line)) = lines.next() {
                    self.set_var(arg.as_ref(), line.trim());
                }
            }
        }
        SUCCESS
    }

    pub(crate) fn disable_plugins(&mut self) { self.flags &= 255 ^ PLUGIN; }

    pub(crate) fn enable_plugins(&mut self) { self.flags |= PLUGIN; }

    pub(crate) fn has_plugin_support(&self) -> bool { self.flags & PLUGIN != 0 }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::{expand_string, Expander};

    struct VariableExpander(pub Variables);

    impl Expander for VariableExpander {
        fn variable(&self, var: &str, _: bool) -> Option<Value> { self.0.get_var(var) }
    }

    #[test]
    fn undefined_variable_expands_to_empty_string() {
        let variables = Variables::default();
        let expanded = expand_string("$FOO", &VariableExpander(variables), false).join("");
        assert_eq!("", &expanded);
    }

    #[test]
    fn set_var_and_expand_a_variable() {
        let mut variables = Variables::default();
        variables.set_var("FOO", "BAR");
        let expanded = expand_string("$FOO", &VariableExpander(variables), false).join("");
        assert_eq!("BAR", &expanded);
    }

    #[test]
    fn decompose_map_reference() {
        if let Some((map_name, inner_key)) = Variables::is_hashmap_reference("map[\'key\']") {
            assert!(map_name == "map".into());
            assert!(inner_key == "key".into());
        } else {
            assert!(false);
        }
    }

    #[test]
    fn minimal_directory_var_should_compact_path() {
        let mut variables = Variables::default();
        variables.set_var("PWD", "/var/log/nix");
        assert_eq!(
            "v/l/nix",
            variables.get_var("MWD").expect("no value returned")
        );
    }

    #[test]
    fn minimal_directory_var_shouldnt_compact_path() {
        let mut variables = Variables::default();
        variables.set_var("PWD", "/var/log");
        assert_eq!(
            "/var/log",
            variables.get_var("MWD").expect("no value returned")
        );
    }
}
<commit_msg>Support '.' characters in aliases/variables (Fixes #770) (#772)<commit_after>use super::{
    colors::Colors, directory_stack::DirectoryStack, plugins::namespaces::{self, StringNamespace},
    status::{FAILURE, SUCCESS},
};
use fnv::FnvHashMap;
use liner::Context;
use std::{
    env, io::{self, BufRead},
};
use sys::{self, geteuid, getpid, getuid, is_root, variables as self_sys};
use types::{
    Array, ArrayVariableContext, HashMap, HashMapVariableContext, Identifier, Key, Value,
    VariableContext,
};
use unicode_segmentation::UnicodeSegmentation;
use xdg::BaseDirectories;

lazy_static! {
    static ref STRING_NAMESPACES: FnvHashMap<Identifier, StringNamespace> = namespaces::collect();
}

#[derive(Clone, Debug)]
pub struct Variables {
    pub hashmaps:  HashMapVariableContext,
    pub arrays:    ArrayVariableContext,
    pub variables: VariableContext,
    pub aliases:   VariableContext,
    flags:         u8,
}

impl Default for Variables {
    fn default() -> Variables {
        let mut map = FnvHashMap::with_capacity_and_hasher(64, Default::default());
        map.insert("DIRECTORY_STACK_SIZE".into(), "1000".into());
        map.insert("HISTORY_SIZE".into(), "1000".into());
        map.insert("HISTFILE_SIZE".into(), "100000".into());
        map.insert(
            "PROMPT".into(),
            "${x::1B}]0;${USER}: \
             ${PWD}${x::07}${c::0x55,bold}${USER}${c::default}:${c::0x4B}${SWD}${c::default}# \
             ${c::reset}"
                .into(),
        );

        // Set the PID, UID, and EUID variables.
        map.insert(
            "PID".into(),
            getpid().ok().map_or("?".into(), |id| id.to_string()),
        );
        map.insert(
            "UID".into(),
            getuid().ok().map_or("?".into(), |id| id.to_string()),
        );
        map.insert(
            "EUID".into(),
            geteuid().ok().map_or("?".into(), |id| id.to_string()),
        );

        // Initialize the HISTFILE variable
        if let Ok(base_dirs) = BaseDirectories::with_prefix("ion") {
            if let Ok(path) = base_dirs.place_data_file("history") {
                map.insert("HISTFILE".into(), path.to_str().unwrap_or("?").into());
                map.insert("HISTFILE_ENABLED".into(), "1".into());
            }
        }

        // Initialize the PWD (Present Working Directory) variable
        env::current_dir().ok().map_or_else(
            || env::set_var("PWD", "?"),
            |path| env::set_var("PWD", path.to_str().unwrap_or("?")),
        );

        // Initialize the HOME variable
        env::home_dir().map_or_else(
            || env::set_var("HOME", "?"),
            |path| env::set_var("HOME", path.to_str().unwrap_or("?")),
        );
        Variables {
            hashmaps:  FnvHashMap::with_capacity_and_hasher(64, Default::default()),
            arrays:    FnvHashMap::with_capacity_and_hasher(64, Default::default()),
            variables: map,
            aliases:   FnvHashMap::with_capacity_and_hasher(64, Default::default()),
            flags:     0,
        }
    }
}

const PLUGIN: u8 = 1;

impl Variables {
    #[allow(dead_code)]
    pub(crate) fn is_hashmap_reference(key: &str) -> Option<(Identifier, Key)> {
        let mut key_iter = key.split('[');

        if let Some(map_name) = key_iter.next() {
            if Variables::is_valid_variable_name(map_name) {
                if let Some(mut inner_key) = key_iter.next() {
                    if inner_key.ends_with(']') {
                        inner_key = inner_key.split(']').next().unwrap_or("");
                        inner_key = inner_key.trim_matches(|c| c == '\'' || c == '\"');
                        return Some((map_name.into(), inner_key.into()));
                    }
                }
            }
        }
        None
    }

    pub(crate) fn tilde_expansion(&self, word: &str, dir_stack: &DirectoryStack) -> Option<String> {
        let mut chars = word.char_indices();

        let tilde_prefix;
        let remainder;

        loop {
            if let Some((ind, c)) = chars.next() {
                if c == '/' || c == '$' {
                    tilde_prefix = &word[1..ind];
                    remainder = &word[ind..];
                    break;
                }
            } else {
                tilde_prefix = &word[1..];
                remainder = "";
                break;
            }
        }

        match tilde_prefix {
            "" => if let Some(home) = env::home_dir() {
                return Some(home.to_string_lossy().to_string() + remainder);
            },
            "+" => if let Some(pwd) = self.get_var("PWD") {
                return Some(pwd.to_string() + remainder);
            } else if let Ok(pwd) = env::current_dir() {
                return Some(pwd.to_string_lossy().to_string() + remainder);
            },
            "-" => if let Some(oldpwd) = self.get_var("OLDPWD") {
                return Some(oldpwd.to_string() + remainder);
            },
            _ => {
                let neg;
                let tilde_num;

                if tilde_prefix.starts_with('+') {
                    tilde_num = &tilde_prefix[1..];
                    neg = false;
                } else if tilde_prefix.starts_with('-') {
                    tilde_num = &tilde_prefix[1..];
                    neg = true;
                } else {
                    tilde_num = tilde_prefix;
                    neg = false;
                }

                match tilde_num.parse() {
                    Ok(num) => {
                        let res = if neg {
                            dir_stack.dir_from_top(num)
                        } else {
                            dir_stack.dir_from_bottom(num)
                        };

                        if let Some(path) = res {
                            return Some(path.to_str().unwrap().to_string());
                        }
                    }
                    Err(_) => if let Some(home) = self_sys::get_user_home(tilde_prefix) {
                        return Some(home + remainder);
                    },
                }
            }
        }
        None
    }

    pub(crate) fn is_valid_variable_name(name: &str) -> bool {
        name.chars().all(Variables::is_valid_variable_character)
    }

    pub(crate) fn is_valid_variable_character(c: char) -> bool {
        c.is_alphanumeric() || c == '_' || c == '?' || c == '.'
    }

    pub fn strings<'a>(&'a self) -> impl Iterator<Item = Identifier> + 'a {
        self.variables
            .keys()
            .cloned()
            .chain(env::vars().map(|(k, _)| k.into()))
    }

    pub fn unset_var(&mut self, name: &str) -> Option<Value> { self.variables.remove(name) }

    pub fn get_var_or_empty(&self, name: &str) -> Value { self.get_var(name).unwrap_or_default() }

    pub fn get_var(&self, name: &str) -> Option<Value> {
        match name {
            "MWD" => return Some(self.get_minimal_directory()),
            "SWD" => return Some(self.get_simplified_directory()),
            _ => (),
        }
        if let Some((name, variable)) = name.find("::").map(|pos| (&name[..pos], &name[pos + 2..]))
        {
            // If the parsed name contains the '::' pattern, then a namespace was
            // designated. Find it.
            match name {
                "c" | "color" => Colors::collect(variable).into_string(),
                "x" | "hex" => match u8::from_str_radix(variable, 16) {
                    Ok(c) => Some((c as char).to_string()),
                    Err(why) => {
                        eprintln!("ion: hex parse error: {}: {}", variable, why);
                        None
                    }
                },
                "env" => env::var(variable).map(Into::into).ok(),
                _ => {
                    if is_root() {
                        eprintln!("ion: root is not allowed to execute plugins");
                        return None;
                    }

                    if !self.has_plugin_support() {
                        eprintln!(
                            "ion: plugins are disabled. Considering enabling them with `let \
                             NS_PLUGINS = 1`"
                        );
                        return None;
                    }

                    // Attempt to obtain the given namespace from our lazily-generated map of
                    // namespaces.
                    if let Some(namespace) = STRING_NAMESPACES.get(name.into()) {
                        // Attempt to execute the given function from that namespace, and map it's
                        // results.
                        match namespace.execute(variable.into()) {
                            Ok(value) => value.map(Into::into),
                            Err(why) => {
                                eprintln!("ion: string namespace error: {}: {}", name, why);
                                None
                            }
                        }
                    } else {
                        eprintln!("ion: unsupported namespace: '{}'", name);
                        None
                    }
                }
            }
        } else {
            // Otherwise, it's just a simple variable name.
            self.variables
                .get(name)
                .cloned()
                .or_else(|| env::var(name).map(Into::into).ok())
        }
    }

    /// Obtains the value for the **MWD** variable.
    ///
    /// Further minimizes the directory path in the same manner that Fish does by default.
    /// That is, if more than two parents are visible in the path, all parent directories
    /// of the current directory will be reduced to a single character.
    fn get_minimal_directory(&self) -> Value {
        let swd = self.get_simplified_directory();

        {
            // Temporarily borrow the `swd` variable while we attempt to assemble a minimal
            // variant of the directory path. If that is not possible, we will cancel the
            // borrow and return `swd` itself as the minified path.
            let elements = swd
                .split("/")
                .filter(|s| !s.is_empty())
                .collect::<Vec<&str>>();
            if elements.len() > 2 {
                let mut output = String::new();
                for element in &elements[0..elements.len() - 1] {
                    let mut segmenter = UnicodeSegmentation::graphemes(*element, true);
                    let grapheme = segmenter.next().unwrap();
                    output.push_str(grapheme);
                    if grapheme == "." {
                        output.push_str(segmenter.next().unwrap());
                    }
                    output.push('/');
                }
                output.push_str(&elements[elements.len() - 1]);
                return output;
            }
        }

        swd
    }

    /// Obtains the value for the **SWD** variable.
    ///
    /// Useful for getting smaller prompts, this will produce a simplified variant of the
    /// working directory which the leading `HOME` prefix replaced with a tilde character.
    fn get_simplified_directory(&self) -> Value {
        self.get_var("PWD")
            .unwrap()
            .replace(&self.get_var("HOME").unwrap(), "~")
    }

    pub fn unset_array(&mut self, name: &str) -> Option<Array> { self.arrays.remove(name) }

    pub fn get_array(&self, name: &str) -> Option<&Array> { self.arrays.get(name) }

    pub fn get_map(&self, name: &str) -> Option<&HashMap> { self.hashmaps.get(name) }

    #[allow(dead_code)]
    pub(crate) fn set_hashmap_value(&mut self, name: &str, key: &str, value: &str) {
        if !name.is_empty() {
            if let Some(map) = self.hashmaps.get_mut(name) {
                map.insert(key.into(), value.into());
                return;
            }

            let mut map = HashMap::with_capacity_and_hasher(4, Default::default());
            map.insert(key.into(), value.into());
            self.hashmaps.insert(name.into(), map);
        }
    }

    pub fn set_array(&mut self, name: &str, value: Array) {
        if !name.is_empty() {
            if value.is_empty() {
                self.arrays.remove(name);
            } else {
                self.arrays.insert(name.into(), value);
            }
        }
    }

    pub fn set_var(&mut self, name: &str, value: &str) {
        if !name.is_empty() {
            if value.is_empty() {
                self.variables.remove(name);
            } else {
                if name == "NS_PLUGINS" {
                    match value {
                        "0" => self.disable_plugins(),
                        "1" => self.enable_plugins(),
                        _ => eprintln!(
                            "ion: unsupported value for NS_PLUGINS. Value must be either 0 or 1."
                        ),
                    }
                    return;
                }
                self.variables.insert(name.into(), value.into());
            }
        }
    }

    pub(crate) fn read<I: IntoIterator>(&mut self, args: I) -> i32
    where
        I::Item: AsRef<str>,
    {
        if sys::isatty(sys::STDIN_FILENO) {
            let mut con = Context::new();
            for arg in args.into_iter().skip(1) {
                match con.read_line(format!("{}=", arg.as_ref().trim()), &mut |_| {}) {
                    Ok(buffer) => self.set_var(arg.as_ref(), buffer.trim()),
                    Err(_) => return FAILURE,
                }
            }
        } else {
            let stdin = io::stdin();
            let handle = stdin.lock();
            let mut lines = handle.lines();
            for arg in args.into_iter().skip(1) {
                if let Some(Ok(line)) = lines.next() {
                    self.set_var(arg.as_ref(), line.trim());
                }
            }
        }
        SUCCESS
    }

    pub(crate) fn disable_plugins(&mut self) { self.flags &= 255 ^ PLUGIN; }

    pub(crate) fn enable_plugins(&mut self) { self.flags |= PLUGIN; }

    pub(crate) fn has_plugin_support(&self) -> bool { self.flags & PLUGIN != 0 }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::{expand_string, Expander};

    struct VariableExpander(pub Variables);

    impl Expander for VariableExpander {
        fn variable(&self, var: &str, _: bool) -> Option<Value> { self.0.get_var(var) }
    }

    #[test]
    fn undefined_variable_expands_to_empty_string() {
        let variables = Variables::default();
        let expanded = expand_string("$FOO", &VariableExpander(variables), false).join("");
        assert_eq!("", &expanded);
    }

    #[test]
    fn set_var_and_expand_a_variable() {
        let mut variables = Variables::default();
        variables.set_var("FOO", "BAR");
        let expanded = expand_string("$FOO", &VariableExpander(variables), false).join("");
        assert_eq!("BAR", &expanded);
    }

    #[test]
    fn decompose_map_reference() {
        if let Some((map_name, inner_key)) = Variables::is_hashmap_reference("map[\'key\']") {
            assert!(map_name == "map".into());
            assert!(inner_key == "key".into());
        } else {
            assert!(false);
        }
    }

    #[test]
    fn minimal_directory_var_should_compact_path() {
        let mut variables = Variables::default();
        variables.set_var("PWD", "/var/log/nix");
        assert_eq!(
            "v/l/nix",
            variables.get_var("MWD").expect("no value returned")
        );
    }

    #[test]
    fn minimal_directory_var_shouldnt_compact_path() {
        let mut variables = Variables::default();
        variables.set_var("PWD", "/var/log");
        assert_eq!(
            "/var/log",
            variables.get_var("MWD").expect("no value returned")
        );
    }
}
<|endoftext|>


<commit_before>use std::iter::repeat;
use test::Bencher;

#[bench]
fn bench_with_capacity(b: &mut Bencher) {
    b.iter(|| String::with_capacity(100));
}

#[bench]
fn bench_push_str(b: &mut Bencher) {
    let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb";
    b.iter(|| {
        let mut r = String::new();
        r.push_str(s);
    });
}

const REPETITIONS: u64 = 10_000;

#[bench]
fn bench_push_str_one_byte(b: &mut Bencher) {
    b.bytes = REPETITIONS;
    b.iter(|| {
        let mut r = String::new();
        for _ in 0..REPETITIONS {
            r.push_str("a")
        }
    });
}

#[bench]
fn bench_push_char_one_byte(b: &mut Bencher) {
    b.bytes = REPETITIONS;
    b.iter(|| {
        let mut r = String::new();
        for _ in 0..REPETITIONS {
            r.push('a')
        }
    });
}

#[bench]
fn bench_push_char_two_bytes(b: &mut Bencher) {
    b.bytes = REPETITIONS * 2;
    b.iter(|| {
        let mut r = String::new();
        for _ in 0..REPETITIONS {
            r.push('â')
        }
    });
}

#[bench]
fn from_utf8_lossy_100_ascii(b: &mut Bencher) {
    let s = b"Hello there, the quick brown fox jumped over the lazy dog! \
              Lorem ipsum dolor sit amet, consectetur. ";

    assert_eq!(100, s.len());
    b.iter(|| {
        let _ = String::from_utf8_lossy(s);
    });
}

#[bench]
fn from_utf8_lossy_100_multibyte(b: &mut Bencher) {
    let s = "𐌀𐌖𐌋𐌄𐌑𐌉ปรدولة الكويتทศไทย中华𐍅𐌿𐌻𐍆𐌹𐌻𐌰".as_bytes();
    assert_eq!(100, s.len());
    b.iter(|| {
        let _ = String::from_utf8_lossy(s);
    });
}

#[bench]
fn from_utf8_lossy_invalid(b: &mut Bencher) {
    let s = b"Hello\xC0\x80 There\xE6\x83 Goodbye";
    b.iter(|| {
        let _ = String::from_utf8_lossy(s);
    });
}

#[bench]
fn from_utf8_lossy_100_invalid(b: &mut Bencher) {
    let s = repeat(0xf5).take(100).collect::<Vec<_>>();
    b.iter(|| {
        let _ = String::from_utf8_lossy(&s);
    });
}

#[bench]
fn bench_exact_size_shrink_to_fit(b: &mut Bencher) {
    let s = "Hello there, the quick brown fox jumped over the lazy dog! \
             Lorem ipsum dolor sit amet, consectetur. ";
    // ensure our operation produces an exact-size string before we benchmark it
    let mut r = String::with_capacity(s.len());
    r.push_str(s);
    assert_eq!(r.len(), r.capacity());
    b.iter(|| {
        let mut r = String::with_capacity(s.len());
        r.push_str(s);
        r.shrink_to_fit();
        r
    });
}

#[bench]
fn bench_from_str(b: &mut Bencher) {
    let s = "Hello there, the quick brown fox jumped over the lazy dog! \
             Lorem ipsum dolor sit amet, consectetur. ";
    b.iter(|| String::from(s))
}

#[bench]
fn bench_from(b: &mut Bencher) {
    let s = "Hello there, the quick brown fox jumped over the lazy dog! \
             Lorem ipsum dolor sit amet, consectetur. ";
    b.iter(|| String::from(s))
}

#[bench]
fn bench_to_string(b: &mut Bencher) {
    let s = "Hello there, the quick brown fox jumped over the lazy dog! \
             Lorem ipsum dolor sit amet, consectetur. ";
    b.iter(|| s.to_string())
}

#[bench]
fn bench_insert_char_short(b: &mut Bencher) {
    let s = "Hello, World!";
    b.iter(|| {
        let mut x = String::from(s);
        black_box(&mut x).insert(6, black_box(' '));
        x
    })
}

#[bench]
fn bench_insert_char_long(b: &mut Bencher) {
    let s = "Hello, World!";
    b.iter(|| {
        let mut x = String::from(s);
        black_box(&mut x).insert(6, black_box('❤'));
        x
    })
}

#[bench]
fn bench_insert_str_short(b: &mut Bencher) {
    let s = "Hello, World!";
    b.iter(|| {
        let mut x = String::from(s);
        black_box(&mut x).insert_str(6, black_box(" "));
        x
    })
}

#[bench]
fn bench_insert_str_long(b: &mut Bencher) {
    let s = "Hello, World!";
    b.iter(|| {
        let mut x = String::from(s);
        black_box(&mut x).insert_str(6, black_box(" rustic "));
        x
    })
}
<commit_msg>Allow added string.insert benchmarks to compile<commit_after>use std::iter::repeat;
use test::{black_box, Bencher};

#[bench]
fn bench_with_capacity(b: &mut Bencher) {
    b.iter(|| String::with_capacity(100));
}

#[bench]
fn bench_push_str(b: &mut Bencher) {
    let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb";
    b.iter(|| {
        let mut r = String::new();
        r.push_str(s);
    });
}

const REPETITIONS: u64 = 10_000;

#[bench]
fn bench_push_str_one_byte(b: &mut Bencher) {
    b.bytes = REPETITIONS;
    b.iter(|| {
        let mut r = String::new();
        for _ in 0..REPETITIONS {
            r.push_str("a")
        }
    });
}

#[bench]
fn bench_push_char_one_byte(b: &mut Bencher) {
    b.bytes = REPETITIONS;
    b.iter(|| {
        let mut r = String::new();
        for _ in 0..REPETITIONS {
            r.push('a')
        }
    });
}

#[bench]
fn bench_push_char_two_bytes(b: &mut Bencher) {
    b.bytes = REPETITIONS * 2;
    b.iter(|| {
        let mut r = String::new();
        for _ in 0..REPETITIONS {
            r.push('â')
        }
    });
}

#[bench]
fn from_utf8_lossy_100_ascii(b: &mut Bencher) {
    let s = b"Hello there, the quick brown fox jumped over the lazy dog! \
              Lorem ipsum dolor sit amet, consectetur. ";

    assert_eq!(100, s.len());
    b.iter(|| {
        let _ = String::from_utf8_lossy(s);
    });
}

#[bench]
fn from_utf8_lossy_100_multibyte(b: &mut Bencher) {
    let s = "𐌀𐌖𐌋𐌄𐌑𐌉ปรدولة الكويتทศไทย中华𐍅𐌿𐌻𐍆𐌹𐌻𐌰".as_bytes();
    assert_eq!(100, s.len());
    b.iter(|| {
        let _ = String::from_utf8_lossy(s);
    });
}

#[bench]
fn from_utf8_lossy_invalid(b: &mut Bencher) {
    let s = b"Hello\xC0\x80 There\xE6\x83 Goodbye";
    b.iter(|| {
        let _ = String::from_utf8_lossy(s);
    });
}

#[bench]
fn from_utf8_lossy_100_invalid(b: &mut Bencher) {
    let s = repeat(0xf5).take(100).collect::<Vec<_>>();
    b.iter(|| {
        let _ = String::from_utf8_lossy(&s);
    });
}

#[bench]
fn bench_exact_size_shrink_to_fit(b: &mut Bencher) {
    let s = "Hello there, the quick brown fox jumped over the lazy dog! \
             Lorem ipsum dolor sit amet, consectetur. ";
    // ensure our operation produces an exact-size string before we benchmark it
    let mut r = String::with_capacity(s.len());
    r.push_str(s);
    assert_eq!(r.len(), r.capacity());
    b.iter(|| {
        let mut r = String::with_capacity(s.len());
        r.push_str(s);
        r.shrink_to_fit();
        r
    });
}

#[bench]
fn bench_from_str(b: &mut Bencher) {
    let s = "Hello there, the quick brown fox jumped over the lazy dog! \
             Lorem ipsum dolor sit amet, consectetur. ";
    b.iter(|| String::from(s))
}

#[bench]
fn bench_from(b: &mut Bencher) {
    let s = "Hello there, the quick brown fox jumped over the lazy dog! \
             Lorem ipsum dolor sit amet, consectetur. ";
    b.iter(|| String::from(s))
}

#[bench]
fn bench_to_string(b: &mut Bencher) {
    let s = "Hello there, the quick brown fox jumped over the lazy dog! \
             Lorem ipsum dolor sit amet, consectetur. ";
    b.iter(|| s.to_string())
}

#[bench]
fn bench_insert_char_short(b: &mut Bencher) {
    let s = "Hello, World!";
    b.iter(|| {
        let mut x = String::from(s);
        black_box(&mut x).insert(6, black_box(' '));
        x
    })
}

#[bench]
fn bench_insert_char_long(b: &mut Bencher) {
    let s = "Hello, World!";
    b.iter(|| {
        let mut x = String::from(s);
        black_box(&mut x).insert(6, black_box('❤'));
        x
    })
}

#[bench]
fn bench_insert_str_short(b: &mut Bencher) {
    let s = "Hello, World!";
    b.iter(|| {
        let mut x = String::from(s);
        black_box(&mut x).insert_str(6, black_box(" "));
        x
    })
}

#[bench]
fn bench_insert_str_long(b: &mut Bencher) {
    let s = "Hello, World!";
    b.iter(|| {
        let mut x = String::from(s);
        black_box(&mut x).insert_str(6, black_box(" rustic "));
        x
    })
}
<|endoftext|>