diff options
| author | Graydon Hoare <graydon@mozilla.com> | 2011-12-05 16:46:37 -0800 |
|---|---|---|
| committer | Graydon Hoare <graydon@mozilla.com> | 2011-12-06 12:13:04 -0800 |
| commit | 447414f00774d37d934867f5a476cf00e1f95423 (patch) | |
| tree | d328aa66e62f345cb5d7c7d01c8215e8c9324e79 /src/libstd | |
| parent | b513a5a5001b850a153db12d9621d00a70ff929a (diff) | |
| download | rust-447414f00774d37d934867f5a476cf00e1f95423.tar.gz rust-447414f00774d37d934867f5a476cf00e1f95423.zip | |
Establish 'core' library separate from 'std'.
Diffstat (limited to 'src/libstd')
62 files changed, 12758 insertions, 0 deletions
diff --git a/src/libstd/bitv.rs b/src/libstd/bitv.rs new file mode 100644 index 00000000000..4b726703085 --- /dev/null +++ b/src/libstd/bitv.rs @@ -0,0 +1,316 @@ +/* +Module: bitv + +Bitvectors. +*/ + +export t; +export create; +export union; +export intersect; +export assign; +export clone; +export get; +export equal; +export clear; +export set_all; +export invert; +export difference; +export set; +export is_true; +export is_false; +export to_vec; +export to_str; +export eq_vec; + +// FIXME: With recursive object types, we could implement binary methods like +// union, intersection, and difference. At that point, we could write +// an optimizing version of this module that produces a different obj +// for the case where nbits <= 32. + +/* +Type: t + +The bitvector type. +*/ +type t = @{storage: [mutable uint], nbits: uint}; + + +const uint_bits: uint = 32u + (1u << 32u >> 27u); + +/* +Function: create + +Constructs a bitvector. + +Parameters: +nbits - The number of bits in the bitvector +init - If true then the bits are initialized to 1, otherwise 0 +*/ +fn create(nbits: uint, init: bool) -> t { + let elt = if init { !0u } else { 0u }; + let storage = vec::init_elt_mut::<uint>(elt, nbits / uint_bits + 1u); + ret @{storage: storage, nbits: nbits}; +} + +fn process(op: block(uint, uint) -> uint, v0: t, v1: t) -> bool { + let len = vec::len(v1.storage); + assert (vec::len(v0.storage) == len); + assert (v0.nbits == v1.nbits); + let changed = false; + uint::range(0u, len) {|i| + let w0 = v0.storage[i]; + let w1 = v1.storage[i]; + let w = op(w0, w1); + if w0 != w { changed = true; v0.storage[i] = w; } + }; + ret changed; +} + + +fn lor(w0: uint, w1: uint) -> uint { ret w0 | w1; } + +fn union(v0: t, v1: t) -> bool { let sub = lor; ret process(sub, v0, v1); } + +fn land(w0: uint, w1: uint) -> uint { ret w0 & w1; } + +/* +Function: intersect + +Calculates the intersection of two bitvectors + +Sets `v0` to the intersection of `v0` and `v1` + +Preconditions: + +Both bitvectors must be the same length + +Returns: + +True if `v0` was changed +*/ +fn intersect(v0: t, v1: t) -> bool { + let sub = land; + ret process(sub, v0, v1); +} + +fn right(_w0: uint, w1: uint) -> uint { ret w1; } + +/* +Function: assign + +Assigns the value of `v1` to `v0` + +Preconditions: + +Both bitvectors must be the same length + +Returns: + +True if `v0` was changed +*/ +fn assign(v0: t, v1: t) -> bool { let sub = right; ret process(sub, v0, v1); } + +/* +Function: clone + +Makes a copy of a bitvector +*/ +fn clone(v: t) -> t { + let storage = vec::init_elt_mut::<uint>(0u, v.nbits / uint_bits + 1u); + let len = vec::len(v.storage); + uint::range(0u, len) {|i| storage[i] = v.storage[i]; }; + ret @{storage: storage, nbits: v.nbits}; +} + +/* +Function: get + +Retreive the value at index `i` +*/ +fn get(v: t, i: uint) -> bool { + assert (i < v.nbits); + let bits = uint_bits; + let w = i / bits; + let b = i % bits; + let x = 1u & v.storage[w] >> b; + ret x == 1u; +} + +// FIXME: This doesn't account for the actual size of the vectors, +// so it could end up comparing garbage bits +/* +Function: equal + +Compares two bitvectors + +Preconditions: + +Both bitvectors must be the same length + +Returns: + +True if both bitvectors contain identical elements +*/ +fn equal(v0: t, v1: t) -> bool { + // FIXME: when we can break or return from inside an iterator loop, + // we can eliminate this painful while-loop + + let len = vec::len(v1.storage); + let i = 0u; + while i < len { + if v0.storage[i] != v1.storage[i] { ret false; } + i = i + 1u; + } + ret true; +} + +/* +Function: clear + +Set all bits to 0 +*/ +fn clear(v: t) { + uint::range(0u, vec::len(v.storage)) {|i| v.storage[i] = 0u; }; +} + +/* +Function: set_all + +Set all bits to 1 +*/ +fn set_all(v: t) { + uint::range(0u, v.nbits) {|i| set(v, i, true); }; +} + +/* +Function: invert + +Invert all bits +*/ +fn invert(v: t) { + uint::range(0u, vec::len(v.storage)) {|i| + v.storage[i] = !v.storage[i]; + }; +} + +/* +Function: difference + +Calculate the difference between two bitvectors + +Sets each element of `v0` to the value of that element minus the element +of `v1` at the same index. + +Preconditions: + +Both bitvectors must be the same length + +Returns: + +True if `v0` was changed +*/ +fn difference(v0: t, v1: t) -> bool { + invert(v1); + let b = intersect(v0, v1); + invert(v1); + ret b; +} + +/* +Function: set + +Set the value of a bit at a given index + +Preconditions: + +`i` must be less than the length of the bitvector +*/ +fn set(v: t, i: uint, x: bool) { + assert (i < v.nbits); + let bits = uint_bits; + let w = i / bits; + let b = i % bits; + let flag = 1u << b; + v.storage[w] = if x { v.storage[w] | flag } else { v.storage[w] & !flag }; +} + + +/* +Function: is_true + +Returns true if all bits are 1 +*/ +fn is_true(v: t) -> bool { + for i: uint in to_vec(v) { if i != 1u { ret false; } } + ret true; +} + + +/* +Function: is_false + +Returns true if all bits are 0 +*/ +fn is_false(v: t) -> bool { + for i: uint in to_vec(v) { if i == 1u { ret false; } } + ret true; +} + +fn init_to_vec(v: t, i: uint) -> uint { ret if get(v, i) { 1u } else { 0u }; } + +/* +Function: to_vec + +Converts the bitvector to a vector of uint with the same length. Each uint +in the resulting vector has either value 0u or 1u. +*/ +fn to_vec(v: t) -> [uint] { + let sub = bind init_to_vec(v, _); + ret vec::init_fn::<uint>(sub, v.nbits); +} + +/* +Function: to_str + +Converts the bitvector to a string. The resulting string has the same +length as the bitvector, and each character is either '0' or '1'. +*/ +fn to_str(v: t) -> str { + let rs = ""; + for i: uint in to_vec(v) { if i == 1u { rs += "1"; } else { rs += "0"; } } + ret rs; +} + +/* +Function: eq_vec + +Compare a bitvector to a vector of uint. The uint vector is expected to +only contain the values 0u and 1u. + +Preconditions: + +Both the bitvector and vector must have the same length +*/ +fn eq_vec(v0: t, v1: [uint]) -> bool { + assert (v0.nbits == vec::len::<uint>(v1)); + let len = v0.nbits; + let i = 0u; + while i < len { + let w0 = get(v0, i); + let w1 = v1[i]; + if !w0 && w1 != 0u || w0 && w1 == 0u { ret false; } + i = i + 1u; + } + ret true; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/libstd/bool.rs b/src/libstd/bool.rs new file mode 100644 index 00000000000..0fc869d1b41 --- /dev/null +++ b/src/libstd/bool.rs @@ -0,0 +1,134 @@ +// -*- rust -*- + +/* +Module: bool + +Classic Boolean logic reified as ADT +*/ + +export t; +export not, and, or, xor, implies; +export eq, ne, is_true, is_false; +export from_str, to_str, all_values, to_bit; + +/* +Type: t + +The type of boolean logic values +*/ +type t = bool; + +/* Function: not + +Negation/Inverse +*/ +pure fn not(v: t) -> t { !v } + +/* Function: and + +Conjunction +*/ +pure fn and(a: t, b: t) -> t { a && b } + +/* Function: or + +Disjunction +*/ +pure fn or(a: t, b: t) -> t { a || b } + +/* +Function: xor + +Exclusive or, i.e. `or(and(a, not(b)), and(not(a), b))` +*/ +pure fn xor(a: t, b: t) -> t { (a && !b) || (!a && b) } + +/* +Function: implies + +Implication in the logic, i.e. from `a` follows `b` +*/ +pure fn implies(a: t, b: t) -> t { !a || b } + +/* +Predicate: eq + +Returns: + +true if truth values `a` and `b` are indistinguishable in the logic +*/ +pure fn eq(a: t, b: t) -> bool { a == b } + +/* +Predicate: ne + +Returns: + +true if truth values `a` and `b` are distinguishable in the logic +*/ +pure fn ne(a: t, b: t) -> bool { a != b } + +/* +Predicate: is_true + +Returns: + +true if `v` represents truth in the logic +*/ +pure fn is_true(v: t) -> bool { v } + +/* +Predicate: is_false + +Returns: + +true if `v` represents falsehood in the logic +*/ +pure fn is_false(v: t) -> bool { !v } + +/* +Function: from_str + +Parse logic value from `s` +*/ +pure fn from_str(s: str) -> t { + alt s { + "true" { true } + "false" { false } + } +} + +/* +Function: to_str + +Convert `v` into a string +*/ +pure fn to_str(v: t) -> str { if v { "true" } else { "false" } } + +/* +Function: all_values + +Iterates over all truth values by passing them to `blk` +in an unspecified order +*/ +fn all_values(blk: block(v: t)) { + blk(true); + blk(false); +} + +/* +Function: to_bit + +Returns: + +An u8 whose first bit is set if `if_true(v)` holds +*/ +fn to_bit(v: t) -> u8 { if v { 1u8 } else { 0u8 } } + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/box.rs b/src/libstd/box.rs new file mode 100644 index 00000000000..6682fe17284 --- /dev/null +++ b/src/libstd/box.rs @@ -0,0 +1,20 @@ +/* +Module: box +*/ + + +export ptr_eq; + +/* +Function: ptr_eq + +Determine if two shared boxes point to the same object +*/ +fn ptr_eq<T>(a: @T, b: @T) -> bool { + // FIXME: ptr::addr_of + unsafe { + let a_ptr: uint = unsafe::reinterpret_cast(a); + let b_ptr: uint = unsafe::reinterpret_cast(b); + ret a_ptr == b_ptr; + } +} diff --git a/src/libstd/c_vec.rs b/src/libstd/c_vec.rs new file mode 100644 index 00000000000..ff5f636d103 --- /dev/null +++ b/src/libstd/c_vec.rs @@ -0,0 +1,99 @@ +/* +Module: c_vec + +Library to interface with chunks of memory allocated in C. + +It is often desirable to safely interface with memory allocated from C, +encapsulating the unsafety into allocation and destruction time. Indeed, +allocating memory externally is currently the only way to give Rust shared +mutable state with C programs that keep their own references; vectors are +unsuitable because they could be reallocated or moved at any time, and +importing C memory into a vector takes a one-time snapshot of the memory. + +This module simplifies the usage of such external blocks of memory. Memory +is encapsulated into an opaque object after creation; the lifecycle of the +memory can be optionally managed by Rust, if an appropriate destructor +closure is provided. Safety is ensured by bounds-checking accesses, which +are marshalled through get and set functions. + +There are three unsafe functions: the two introduction forms, and the +pointer elimination form. The introduction forms are unsafe for the obvious +reason (they act on a pointer that cannot be checked inside the method), but +the elimination form is somewhat more subtle in its unsafety. By using a +pointer taken from a c_vec::t without keeping a reference to the c_vec::t +itself around, the c_vec could be garbage collected, and the memory within +could be destroyed. There are legitimate uses for the pointer elimination +form -- for instance, to pass memory back into C -- but great care must be +taken to ensure that a reference to the c_vec::t is still held if needed. + + */ + +export t; +export create, create_with_dtor; +export get, set; +export size; +export ptr; + +/* + Type: t + + The type representing a native chunk of memory. Wrapped in a tag for + opacity; FIXME #818 when it is possible to have truly opaque types, this + should be revisited. + */ + +tag t<T> { + t({ base: *mutable T, size: uint, rsrc: @dtor_res}); +} + +resource dtor_res(dtor: option::t<fn@()>) { + alt dtor { + option::none. { } + option::some(f) { f(); } + } +} + +/* + Section: Introduction forms + */ + +unsafe fn create<T>(base: *mutable T, size: uint) -> t<T> { + ret t({base: base, + size: size, + rsrc: @dtor_res(option::none) + }); +} + +unsafe fn create_with_dtor<T>(base: *mutable T, size: uint, dtor: fn@()) + -> t<T> { + ret t({base: base, + size: size, + rsrc: @dtor_res(option::some(dtor)) + }); +} + +/* + Section: Operations + */ + +fn get<copy T>(t: t<T>, ofs: uint) -> T { + assert ofs < (*t).size; + ret unsafe { *ptr::mut_offset((*t).base, ofs) }; +} + +fn set<copy T>(t: t<T>, ofs: uint, v: T) { + assert ofs < (*t).size; + unsafe { *ptr::mut_offset((*t).base, ofs) = v }; +} + +/* + Section: Elimination forms + */ + +fn size<T>(t: t<T>) -> uint { + ret (*t).size; +} + +unsafe fn ptr<T>(t: t<T>) -> *mutable T { + ret (*t).base; +} diff --git a/src/libstd/char.rs b/src/libstd/char.rs new file mode 100644 index 00000000000..bc8e74355c6 --- /dev/null +++ b/src/libstd/char.rs @@ -0,0 +1,137 @@ +/* +Module: char + +Utilities for manipulating the char type +*/ + +/* +Function: is_whitespace + +Indicates whether a character is whitespace. + +Whitespace characters include space (U+0020), tab (U+0009), line feed +(U+000A), carriage return (U+000D), and a number of less common +ASCII and unicode characters. +*/ +pure fn is_whitespace(c: char) -> bool { + const ch_space: char = '\u0020'; + const ch_ogham_space_mark: char = '\u1680'; + const ch_mongolian_vowel_sep: char = '\u180e'; + const ch_en_quad: char = '\u2000'; + const ch_em_quad: char = '\u2001'; + const ch_en_space: char = '\u2002'; + const ch_em_space: char = '\u2003'; + const ch_three_per_em_space: char = '\u2004'; + const ch_four_per_em_space: char = '\u2005'; + const ch_six_per_em_space: char = '\u2006'; + const ch_figure_space: char = '\u2007'; + const ch_punctuation_space: char = '\u2008'; + const ch_thin_space: char = '\u2009'; + const ch_hair_space: char = '\u200a'; + const ch_narrow_no_break_space: char = '\u202f'; + const ch_medium_mathematical_space: char = '\u205f'; + const ch_ideographic_space: char = '\u3000'; + const ch_line_separator: char = '\u2028'; + const ch_paragraph_separator: char = '\u2029'; + const ch_character_tabulation: char = '\u0009'; + const ch_line_feed: char = '\u000a'; + const ch_line_tabulation: char = '\u000b'; + const ch_form_feed: char = '\u000c'; + const ch_carriage_return: char = '\u000d'; + const ch_next_line: char = '\u0085'; + const ch_no_break_space: char = '\u00a0'; + + if c == ch_space { + true + } else if c == ch_ogham_space_mark { + true + } else if c == ch_mongolian_vowel_sep { + true + } else if c == ch_en_quad { + true + } else if c == ch_em_quad { + true + } else if c == ch_en_space { + true + } else if c == ch_em_space { + true + } else if c == ch_three_per_em_space { + true + } else if c == ch_four_per_em_space { + true + } else if c == ch_six_per_em_space { + true + } else if c == ch_figure_space { + true + } else if c == ch_punctuation_space { + true + } else if c == ch_thin_space { + true + } else if c == ch_hair_space { + true + } else if c == ch_narrow_no_break_space { + true + } else if c == ch_medium_mathematical_space { + true + } else if c == ch_ideographic_space { + true + } else if c == ch_line_tabulation { + true + } else if c == ch_paragraph_separator { + true + } else if c == ch_character_tabulation { + true + } else if c == ch_line_feed { + true + } else if c == ch_line_tabulation { + true + } else if c == ch_form_feed { + true + } else if c == ch_carriage_return { + true + } else if c == ch_next_line { + true + } else if c == ch_no_break_space { true } else { false } +} + +/* + Function: to_digit + + Convert a char to the corresponding digit. + + Parameters: + c - a char, either '0' to '9', 'a' to 'z' or 'A' to 'Z' + + Returns: + If `c` is between '0' and '9', the corresponding value between 0 and 9. + If `c` is 'a' or 'A', 10. If `c` is 'b' or 'B', 11, etc. + + Safety note: + This function fails if `c` is not a valid char +*/ +pure fn to_digit(c: char) -> u8 { + alt c { + '0' to '9' { c as u8 - ('0' as u8) } + 'a' to 'z' { c as u8 + 10u8 - ('a' as u8) } + 'A' to 'Z' { c as u8 + 10u8 - ('A' as u8) } + _ { fail; } + } +} + +/* + Function: cmp + + Compare two chars. + + Parameters: + a - a char + b - a char + + Returns: + -1 if a<b, 0 if a==b, +1 if a>b +*/ +fn cmp(a: char, b: char) -> int { + ret if b > a { -1 } + else if b < a { 1 } + else { 0 } +} \ No newline at end of file diff --git a/src/libstd/cmath.rs b/src/libstd/cmath.rs new file mode 100644 index 00000000000..69ecbac3e91 --- /dev/null +++ b/src/libstd/cmath.rs @@ -0,0 +1,71 @@ +import ctypes::c_int; + +#[link_name = "m"] +#[abi = "cdecl"] +native mod f64 { + + // Alpabetically sorted by link_name + + pure fn acos(n: f64) -> f64; + pure fn asin(n: f64) -> f64; + pure fn atan(n: f64) -> f64; + pure fn atan2(a: f64, b: f64) -> f64; + pure fn ceil(n: f64) -> f64; + pure fn cos(n: f64) -> f64; + pure fn cosh(n: f64) -> f64; + pure fn exp(n: f64) -> f64; + #[link_name="fabs"] pure fn abs(n: f64) -> f64; + pure fn floor(n: f64) -> f64; + pure fn fmod(x: f64, y: f64) -> f64; + pure fn frexp(n: f64, &value: c_int) -> f64; + pure fn ldexp(x: f64, n: c_int) -> f64; + #[link_name="log"] pure fn ln(n: f64) -> f64; + #[link_name="log1p"] pure fn ln1p(n: f64) -> f64; + pure fn log10(n: f64) -> f64; + pure fn log2(n: f64) -> f64; + pure fn modf(n: f64, &iptr: f64) -> f64; + pure fn pow(n: f64, e: f64) -> f64; + pure fn rint(n: f64) -> f64; + pure fn round(n: f64) -> f64; + pure fn sin(n: f64) -> f64; + pure fn sinh(n: f64) -> f64; + pure fn sqrt(n: f64) -> f64; + pure fn tan(n: f64) -> f64; + pure fn tanh(n: f64) -> f64; + pure fn trunc(n: f64) -> f64; +} + +#[link_name = "m"] +#[abi = "cdecl"] +native mod f32 { + + // Alpabetically sorted by link_name + + #[link_name="acosf"] pure fn acos(n: f32) -> f32; + #[link_name="asinf"] pure fn asin(n: f32) -> f32; + #[link_name="atanf"] pure fn atan(n: f32) -> f32; + #[link_name="atan2f"] pure fn atan2(a: f32, b: f32) -> f32; + #[link_name="ceilf"] pure fn ceil(n: f32) -> f32; + #[link_name="cosf"] pure fn cos(n: f32) -> f32; + #[link_name="coshf"] pure fn cosh(n: f32) -> f32; + #[link_name="expf"] pure fn exp(n: f32) -> f32; + #[link_name="fabsf"] pure fn abs(n: f32) -> f32; + #[link_name="floorf"] pure fn floor(n: f32) -> f32; + #[link_name="frexpf"] pure fn frexp(n: f64, &value: c_int) -> f32; + #[link_name="fmodf"] pure fn fmod(x: f32, y: f32) -> f32; + #[link_name="ldexpf"] pure fn ldexp(x: f32, n: c_int) -> f32; + #[link_name="logf"] pure fn ln(n: f32) -> f32; + #[link_name="log1p"] pure fn ln1p(n: f64) -> f64; + #[link_name="log2f"] pure fn log2(n: f32) -> f32; + #[link_name="log10f"] pure fn log10(n: f32) -> f32; + #[link_name="modff"] pure fn modf(n: f32, &iptr: f32) -> f32; + #[link_name="powf"] pure fn pow(n: f32, e: f32) -> f32; + #[link_name="rintf"] pure fn rint(n: f32) -> f32; + #[link_name="roundf"] pure fn round(n: f32) -> f32; + #[link_name="sinf"] pure fn sin(n: f32) -> f32; + #[link_name="sinhf"] pure fn sinh(n: f32) -> f32; + #[link_name="sqrtf"] pure fn sqrt(n: f32) -> f32; + #[link_name="tanf"] pure fn tan(n: f32) -> f32; + #[link_name="tanhf"] pure fn tanh(n: f32) -> f32; + #[link_name="truncf"] pure fn trunc(n: f32) -> f32; +} diff --git a/src/libstd/comm.rs b/src/libstd/comm.rs new file mode 100644 index 00000000000..e00205a522b --- /dev/null +++ b/src/libstd/comm.rs @@ -0,0 +1,184 @@ +/* +Module: comm + +Communication between tasks + +Communication between tasks is facilitated by ports (in the receiving task), +and channels (in the sending task). Any number of channels may feed into a +single port. + +Ports and channels may only transmit values of unique types; that is, +values that are statically guaranteed to be accessed by a single +'owner' at a time. Unique types include scalars, vectors, strings, +and records, tags, tuples and unique boxes (~T) thereof. Most notably, +shared boxes (@T) may not be transmitted across channels. + +Example: + +> use std::{task, comm, io}; +> +> let p = comm::port(); +> task::spawn(comm::chan(p), fn (c: chan<str>) { +> comm::send(c, "Hello, World"); +> }); +> +> io::println(comm::recv(p)); + +*/ + +import sys; +import task; + +export send; +export recv; +export chan; +export port; + +#[abi = "cdecl"] +native mod rustrt { + type void; + type rust_port; + + fn chan_id_send<send T>(t: *sys::type_desc, + target_task: task::task, target_port: port_id, + data: T) -> ctypes::uintptr_t; + + fn new_port(unit_sz: uint) -> *rust_port; + fn del_port(po: *rust_port); + fn rust_port_detach(po: *rust_port); + fn get_port_id(po: *rust_port) -> port_id; + fn rust_port_size(po: *rust_port) -> ctypes::size_t; + fn port_recv(dptr: *uint, po: *rust_port, + yield: *ctypes::uintptr_t, + killed: *ctypes::uintptr_t); +} + +#[abi = "rust-intrinsic"] +native mod rusti { + fn call_with_retptr<send T>(&&f: fn@(*uint)) -> T; +} + +type port_id = int; + +// It's critical that this only have one variant, so it has a record +// layout, and will work in the rust_task structure in task.rs. +/* +Type: chan + +A communication endpoint that can send messages. Channels send +messages to ports. + +Each channel is bound to a port when the channel is constructed, so +the destination port for a channel must exist before the channel +itself. + +Channels are weak: a channel does not keep the port it is bound to alive. +If a channel attempts to send data to a dead port that data will be silently +dropped. + +Channels may be duplicated and themselves transmitted over other channels. +*/ +tag chan<send T> { + chan_t(task::task, port_id); +} + +resource port_ptr<send T>(po: *rustrt::rust_port) { + // Once the port is detached it's guaranteed not to receive further + // messages + rustrt::rust_port_detach(po); + // Drain the port so that all the still-enqueued items get dropped + while rustrt::rust_port_size(po) > 0u { + // FIXME: For some reason if we don't assign to something here + // we end up with invalid reads in the drop glue. + let _t = recv_::<T>(po); + } + rustrt::del_port(po); +} + +/* +Type: port + +A communication endpoint that can receive messages. Ports receive +messages from channels. + +Each port has a unique per-task identity and may not be replicated or +transmitted. If a port value is copied, both copies refer to the same port. + +Ports may be associated with multiple <chan>s. +*/ +tag port<send T> { port_t(@port_ptr<T>); } + +/* +Function: send + +Sends data over a channel. + +The sent data is moved into the channel, whereupon the caller loses access +to it. +*/ +fn send<send T>(ch: chan<T>, -data: T) { + let chan_t(t, p) = ch; + let res = rustrt::chan_id_send(sys::get_type_desc::<T>(), t, p, data); + if res != 0u unsafe { + // Data sent successfully + unsafe::leak(data); + } + task::yield(); +} + +/* +Function: port + +Constructs a port. +*/ +fn port<send T>() -> port<T> { + port_t(@port_ptr(rustrt::new_port(sys::size_of::<T>()))) +} + +/* +Function: recv + +Receive from a port. + +If no data is available on the port then the task will block until data +becomes available. +*/ +fn recv<send T>(p: port<T>) -> T { recv_(***p) } + +// Receive on a raw port pointer +fn recv_<send T>(p: *rustrt::rust_port) -> T { + // FIXME: Due to issue 1185 we can't use a return pointer when + // calling C code, and since we can't create our own return + // pointer on the stack, we're going to call a little intrinsic + // that will grab the value of the return pointer, then call this + // function, which we will then use to call the runtime. + fn recv(dptr: *uint, port: *rustrt::rust_port, + yield: *ctypes::uintptr_t, + killed: *ctypes::uintptr_t) unsafe { + rustrt::port_recv(dptr, port, yield, killed); + } + let yield = 0u; + let yieldp = ptr::addr_of(yield); + let killed = 0u; + let killedp = ptr::addr_of(killed); + let res = rusti::call_with_retptr(bind recv(_, p, yieldp, killedp)); + if killed != 0u { + fail "killed"; + } + if yield != 0u { + // Data isn't available yet, so res has not been initialized. + task::yield(); + } + ret res; +} + +/* +Function: chan + +Constructs a channel. + +The channel is bound to the port used to construct it. +*/ +fn chan<send T>(p: port<T>) -> chan<T> { + chan_t(task::get_task(), rustrt::get_port_id(***p)) +} diff --git a/src/libstd/ctypes.rs b/src/libstd/ctypes.rs new file mode 100644 index 00000000000..82e414a864c --- /dev/null +++ b/src/libstd/ctypes.rs @@ -0,0 +1,42 @@ +/* +Module: ctypes + +Definitions useful for C interop +*/ + +type c_int = i32; +type c_uint = u32; + +type void = int; // Not really the same as C +type long = int; +type unsigned = u32; +type ulong = uint; + +type intptr_t = uint; +type uintptr_t = uint; +type uint32_t = u32; + +// machine type equivalents of rust int, uint, float + +#[cfg(target_arch="x86")] +type m_int = i32; +#[cfg(target_arch="x86_64")] +type m_int = i64; + +#[cfg(target_arch="x86")] +type m_uint = u32; +#[cfg(target_arch="x86_64")] +type m_uint = u64; + +// This *must* match with "import m_float = fXX" in std::math per arch +type m_float = f64; + +type size_t = uint; +type ssize_t = int; +type off_t = uint; + +type fd_t = i32; // not actually a C type, but should be. +type pid_t = i32; + +// enum is implementation-defined, but is 32-bits in practice +type enum = u32; diff --git a/src/libstd/dbg.rs b/src/libstd/dbg.rs new file mode 100644 index 00000000000..ea98150d76a --- /dev/null +++ b/src/libstd/dbg.rs @@ -0,0 +1,71 @@ + + + +/** + * Unsafe debugging functions for inspecting values. + * + * Your RUST_LOG environment variable must contain "stdlib" for any debug + * logging. + */ + +#[abi = "cdecl"] +native mod rustrt { + fn debug_tydesc(td: *sys::type_desc); + fn debug_opaque<T>(td: *sys::type_desc, x: T); + fn debug_box<T>(td: *sys::type_desc, x: @T); + fn debug_tag<T>(td: *sys::type_desc, x: T); + fn debug_obj<T>(td: *sys::type_desc, x: T, nmethods: uint, nbytes: uint); + fn debug_fn<T>(td: *sys::type_desc, x: T); + fn debug_ptrcast<T, U>(td: *sys::type_desc, x: @T) -> @U; +} + +fn debug_tydesc<T>() { + rustrt::debug_tydesc(sys::get_type_desc::<T>()); +} + +fn debug_opaque<T>(x: T) { + rustrt::debug_opaque::<T>(sys::get_type_desc::<T>(), x); +} + +fn debug_box<T>(x: @T) { + rustrt::debug_box::<T>(sys::get_type_desc::<T>(), x); +} + +fn debug_tag<T>(x: T) { + rustrt::debug_tag::<T>(sys::get_type_desc::<T>(), x); +} + + +/** + * `nmethods` is the number of methods we expect the object to have. The + * runtime will print this many words of the obj vtbl). + * + * `nbytes` is the number of bytes of body data we expect the object to have. + * The runtime will print this many bytes of the obj body. You probably want + * this to at least be 4u, since an implicit captured tydesc pointer sits in + * the front of any obj's data tuple.x + */ +fn debug_obj<T>(x: T, nmethods: uint, nbytes: uint) { + rustrt::debug_obj::<T>(sys::get_type_desc::<T>(), x, nmethods, nbytes); +} + +fn debug_fn<T>(x: T) { + rustrt::debug_fn::<T>(sys::get_type_desc::<T>(), x); +} + +unsafe fn ptr_cast<T, U>(x: @T) -> @U { + ret rustrt::debug_ptrcast::<T, U>(sys::get_type_desc::<T>(), x); +} + +fn refcount<T>(a: @T) -> uint unsafe { + let p: *uint = unsafe::reinterpret_cast(a); + ret *p; +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/deque.rs b/src/libstd/deque.rs new file mode 100644 index 00000000000..7c0a13bb5f8 --- /dev/null +++ b/src/libstd/deque.rs @@ -0,0 +1,129 @@ +/* +Module: deque + +A deque. Untested as of yet. Likely buggy. +*/ + +/* +Object: t +*/ +type t<T> = obj { + // Method: size + fn size() -> uint; + // Method: add_front + fn add_front(T); + // Method: add_back + fn add_back(T); + // Method: pop_front + fn pop_front() -> T; + // Method: pop_back + fn pop_back() -> T; + // Method: peek_front + fn peek_front() -> T; + // Method: peek_back + fn peek_back() -> T; + // Method: get + fn get(int) -> T; + }; + +/* +Section: Functions +*/ + +/* +Function: create +*/ +fn create<copy T>() -> t<T> { + type cell<T> = option::t<T>; + + let initial_capacity: uint = 32u; // 2^5 + /** + * Grow is only called on full elts, so nelts is also len(elts), unlike + * elsewhere. + */ + fn grow<copy T>(nelts: uint, lo: uint, elts: [mutable cell<T>]) -> + [mutable cell<T>] { + assert (nelts == vec::len(elts)); + let rv = [mutable]; + + let i = 0u; + let nalloc = uint::next_power_of_two(nelts + 1u); + while i < nalloc { + if i < nelts { + rv += [mutable elts[(lo + i) % nelts]]; + } else { rv += [mutable option::none]; } + i += 1u; + } + + ret rv; + } + fn get<T>(elts: [mutable cell<T>], i: uint) -> T { + ret alt elts[i] { option::some(t) { t } _ { fail } }; + } + obj deque<copy T>(mutable nelts: uint, + mutable lo: uint, + mutable hi: uint, + mutable elts: [mutable cell<T>]) { + fn size() -> uint { ret nelts; } + fn add_front(t: T) { + let oldlo: uint = lo; + if lo == 0u { + lo = vec::len::<cell<T>>(elts) - 1u; + } else { lo -= 1u; } + if lo == hi { + elts = grow::<T>(nelts, oldlo, elts); + lo = vec::len::<cell<T>>(elts) - 1u; + hi = nelts; + } + elts[lo] = option::some::<T>(t); + nelts += 1u; + } + fn add_back(t: T) { + if lo == hi && nelts != 0u { + elts = grow::<T>(nelts, lo, elts); + lo = 0u; + hi = nelts; + } + elts[hi] = option::some::<T>(t); + hi = (hi + 1u) % vec::len::<cell<T>>(elts); + nelts += 1u; + } + + /** + * We actually release (turn to none()) the T we're popping so + * that we don't keep anyone's refcount up unexpectedly. + */ + fn pop_front() -> T { + let t: T = get::<T>(elts, lo); + elts[lo] = option::none::<T>; + lo = (lo + 1u) % vec::len::<cell<T>>(elts); + nelts -= 1u; + ret t; + } + fn pop_back() -> T { + if hi == 0u { + hi = vec::len::<cell<T>>(elts) - 1u; + } else { hi -= 1u; } + let t: T = get::<T>(elts, hi); + elts[hi] = option::none::<T>; + nelts -= 1u; + ret t; + } + fn peek_front() -> T { ret get::<T>(elts, lo); } + fn peek_back() -> T { ret get::<T>(elts, hi - 1u); } + fn get(i: int) -> T { + let idx: uint = (lo + (i as uint)) % vec::len::<cell<T>>(elts); + ret get::<T>(elts, idx); + } + } + let v: [mutable cell<T>] = + vec::init_elt_mut(option::none, initial_capacity); + ret deque::<T>(0u, 0u, 0u, v); +} +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/ebml.rs b/src/libstd/ebml.rs new file mode 100644 index 00000000000..4a49dd473f4 --- /dev/null +++ b/src/libstd/ebml.rs @@ -0,0 +1,175 @@ + + +// Simple Extensible Binary Markup Language (ebml) reader and writer on a +// cursor model. See the specification here: +// http://www.matroska.org/technical/specs/rfc/index.html +import option::{some, none}; + +type ebml_tag = {id: uint, size: uint}; + +type ebml_state = {ebml_tag: ebml_tag, tag_pos: uint, data_pos: uint}; + + +// TODO: When we have module renaming, make "reader" and "writer" separate +// modules within this file. + +// ebml reading +type doc = {data: @[u8], start: uint, end: uint}; + +fn vint_at(data: [u8], start: uint) -> {val: uint, next: uint} { + let a = data[start]; + if a & 0x80u8 != 0u8 { ret {val: a & 0x7fu8 as uint, next: start + 1u}; } + if a & 0x40u8 != 0u8 { + ret {val: (a & 0x3fu8 as uint) << 8u | (data[start + 1u] as uint), + next: start + 2u}; + } else if a & 0x20u8 != 0u8 { + ret {val: + (a & 0x1fu8 as uint) << 16u | + (data[start + 1u] as uint) << 8u | + (data[start + 2u] as uint), + next: start + 3u}; + } else if a & 0x10u8 != 0u8 { + ret {val: + (a & 0x0fu8 as uint) << 24u | + (data[start + 1u] as uint) << 16u | + (data[start + 2u] as uint) << 8u | + (data[start + 3u] as uint), + next: start + 4u}; + } else { log_err "vint too big"; fail; } +} + +fn new_doc(data: @[u8]) -> doc { + ret {data: data, start: 0u, end: vec::len::<u8>(*data)}; +} + +fn doc_at(data: @[u8], start: uint) -> doc { + let elt_tag = vint_at(*data, start); + let elt_size = vint_at(*data, elt_tag.next); + let end = elt_size.next + elt_size.val; + ret {data: data, start: elt_size.next, end: end}; +} + +fn maybe_get_doc(d: doc, tg: uint) -> option::t<doc> { + let pos = d.start; + while pos < d.end { + let elt_tag = vint_at(*d.data, pos); + let elt_size = vint_at(*d.data, elt_tag.next); + pos = elt_size.next + elt_size.val; + if elt_tag.val == tg { + ret some::<doc>({data: d.data, start: elt_size.next, end: pos}); + } + } + ret none::<doc>; +} + +fn get_doc(d: doc, tg: uint) -> doc { + alt maybe_get_doc(d, tg) { + some(d) { ret d; } + none. { + log_err "failed to find block with tag " + uint::to_str(tg, 10u); + fail; + } + } +} + +fn docs(d: doc, it: block(uint, doc)) { + let pos = d.start; + while pos < d.end { + let elt_tag = vint_at(*d.data, pos); + let elt_size = vint_at(*d.data, elt_tag.next); + pos = elt_size.next + elt_size.val; + it(elt_tag.val, {data: d.data, start: elt_size.next, end: pos}); + } +} + +fn tagged_docs(d: doc, tg: uint, it: block(doc)) { + let pos = d.start; + while pos < d.end { + let elt_tag = vint_at(*d.data, pos); + let elt_size = vint_at(*d.data, elt_tag.next); + pos = elt_size.next + elt_size.val; + if elt_tag.val == tg { + it({data: d.data, start: elt_size.next, end: pos}); + } + } +} + +fn doc_data(d: doc) -> [u8] { ret vec::slice::<u8>(*d.data, d.start, d.end); } + +fn be_uint_from_bytes(data: @[u8], start: uint, size: uint) -> uint { + let sz = size; + assert (sz <= 4u); + let val = 0u; + let pos = start; + while sz > 0u { + sz -= 1u; + val += (data[pos] as uint) << sz * 8u; + pos += 1u; + } + ret val; +} + +fn doc_as_uint(d: doc) -> uint { + ret be_uint_from_bytes(d.data, d.start, d.end - d.start); +} + + +// ebml writing +type writer = {writer: io::buf_writer, mutable size_positions: [uint]}; + +fn write_sized_vint(w: io::buf_writer, n: uint, size: uint) { + let buf: [u8]; + alt size { + 1u { buf = [0x80u8 | (n as u8)]; } + 2u { buf = [0x40u8 | (n >> 8u as u8), n & 0xffu as u8]; } + 3u { + buf = + [0x20u8 | (n >> 16u as u8), n >> 8u & 0xffu as u8, + n & 0xffu as u8]; + } + 4u { + buf = + [0x10u8 | (n >> 24u as u8), n >> 16u & 0xffu as u8, + n >> 8u & 0xffu as u8, n & 0xffu as u8]; + } + _ { log_err "vint to write too big"; fail; } + } + w.write(buf); +} + +fn write_vint(w: io::buf_writer, n: uint) { + if n < 0x7fu { write_sized_vint(w, n, 1u); ret; } + if n < 0x4000u { write_sized_vint(w, n, 2u); ret; } + if n < 0x200000u { write_sized_vint(w, n, 3u); ret; } + if n < 0x10000000u { write_sized_vint(w, n, 4u); ret; } + log_err "vint to write too big"; + fail; +} + +fn create_writer(w: io::buf_writer) -> writer { + let size_positions: [uint] = []; + ret {writer: w, mutable size_positions: size_positions}; +} + + +// TODO: Provide a function to write the standard ebml header. +fn start_tag(w: writer, tag_id: uint) { + // Write the tag ID: + + write_vint(w.writer, tag_id); + // Write a placeholder four-byte size. + + w.size_positions += [w.writer.tell()]; + let zeroes: [u8] = [0u8, 0u8, 0u8, 0u8]; + w.writer.write(zeroes); +} + +fn end_tag(w: writer) { + let last_size_pos = vec::pop::<uint>(w.size_positions); + let cur_pos = w.writer.tell(); + w.writer.seek(last_size_pos as int, io::seek_set); + write_sized_vint(w.writer, cur_pos - last_size_pos - 4u, 4u); + w.writer.seek(cur_pos as int, io::seek_set); +} +// TODO: optionally perform "relaxations" on end_tag to more efficiently +// encode sizes; this is a fixed point iteration diff --git a/src/libstd/either.rs b/src/libstd/either.rs new file mode 100644 index 00000000000..89d47b20746 --- /dev/null +++ b/src/libstd/either.rs @@ -0,0 +1,89 @@ +/* +Module: either + +A type that represents one of two alternatives +*/ + + +/* +Tag: t + +The either type +*/ +tag t<T, U> { + /* Variant: left */ + left(T); + /* Variant: right */ + right(U); +} + +/* Section: Operations */ + +/* +Function: either + +Applies a function based on the given either value + +If `value` is left(T) then `f_left` is applied to its contents, if +`value` is right(U) then `f_right` is applied to its contents, and +the result is returned. +*/ +fn either<T, U, + V>(f_left: block(T) -> V, f_right: block(U) -> V, value: t<T, U>) -> + V { + alt value { left(l) { f_left(l) } right(r) { f_right(r) } } +} + +/* +Function: lefts + +Extracts from a vector of either all the left values. +*/ +fn lefts<copy T, U>(eithers: [t<T, U>]) -> [T] { + let result: [T] = []; + for elt: t<T, U> in eithers { + alt elt { left(l) { result += [l]; } _ {/* fallthrough */ } } + } + ret result; +} + +/* +Function: rights + +Extracts from a vector of either all the right values +*/ +fn rights<T, copy U>(eithers: [t<T, U>]) -> [U] { + let result: [U] = []; + for elt: t<T, U> in eithers { + alt elt { right(r) { result += [r]; } _ {/* fallthrough */ } } + } + ret result; +} + +/* +Function: partition + +Extracts from a vector of either all the left values and right values + +Returns a structure containing a vector of left values and a vector of +right values. +*/ +fn partition<copy T, copy U>(eithers: [t<T, U>]) + -> {lefts: [T], rights: [U]} { + let lefts: [T] = []; + let rights: [U] = []; + for elt: t<T, U> in eithers { + alt elt { left(l) { lefts += [l]; } right(r) { rights += [r]; } } + } + ret {lefts: lefts, rights: rights}; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/libstd/extfmt.rs b/src/libstd/extfmt.rs new file mode 100644 index 00000000000..14a014e891a --- /dev/null +++ b/src/libstd/extfmt.rs @@ -0,0 +1,453 @@ +/* +Syntax Extension: fmt + +Format a string + +The 'fmt' extension is modeled on the posix printf system. + +A posix conversion ostensibly looks like this + +> %[parameter][flags][width][.precision][length]type + +Given the different numeric type bestiary we have, we omit the 'length' +parameter and support slightly different conversions for 'type' + +> %[parameter][flags][width][.precision]type + +we also only support translating-to-rust a tiny subset of the possible +combinations at the moment. + +Example: + +log #fmt("hello, %s!", "world"); + +*/ + +import option::{some, none}; + + +/* + * We have a 'ct' (compile-time) module that parses format strings into a + * sequence of conversions. From those conversions AST fragments are built + * that call into properly-typed functions in the 'rt' (run-time) module. + * Each of those run-time conversion functions accepts another conversion + * description that specifies how to format its output. + * + * The building of the AST is currently done in a module inside the compiler, + * but should migrate over here as the plugin interface is defined. + */ + +// Functions used by the fmt extension at compile time +mod ct { + tag signedness { signed; unsigned; } + tag caseness { case_upper; case_lower; } + tag ty { + ty_bool; + ty_str; + ty_char; + ty_int(signedness); + ty_bits; + ty_hex(caseness); + ty_octal; + ty_float; + // FIXME: More types + } + tag flag { + flag_left_justify; + flag_left_zero_pad; + flag_space_for_sign; + flag_sign_always; + flag_alternate; + } + tag count { + count_is(int); + count_is_param(int); + count_is_next_param; + count_implied; + } + + // A formatted conversion from an expression to a string + type conv = + {param: option::t<int>, + flags: [flag], + width: count, + precision: count, + ty: ty}; + + + // A fragment of the output sequence + tag piece { piece_string(str); piece_conv(conv); } + type error_fn = fn@(str) -> ! ; + + fn parse_fmt_string(s: str, error: error_fn) -> [piece] { + let pieces: [piece] = []; + let lim = str::byte_len(s); + let buf = ""; + fn flush_buf(buf: str, &pieces: [piece]) -> str { + if str::byte_len(buf) > 0u { + let piece = piece_string(buf); + pieces += [piece]; + } + ret ""; + } + let i = 0u; + while i < lim { + let curr = str::substr(s, i, 1u); + if str::eq(curr, "%") { + i += 1u; + if i >= lim { + error("unterminated conversion at end of string"); + } + let curr2 = str::substr(s, i, 1u); + if str::eq(curr2, "%") { + i += 1u; + } else { + buf = flush_buf(buf, pieces); + let rs = parse_conversion(s, i, lim, error); + pieces += [rs.piece]; + i = rs.next; + } + } else { buf += curr; i += 1u; } + } + buf = flush_buf(buf, pieces); + ret pieces; + } + fn peek_num(s: str, i: uint, lim: uint) -> + option::t<{num: uint, next: uint}> { + if i >= lim { ret none; } + let c = s[i]; + if !('0' as u8 <= c && c <= '9' as u8) { ret option::none; } + let n = c - ('0' as u8) as uint; + ret alt peek_num(s, i + 1u, lim) { + none. { some({num: n, next: i + 1u}) } + some(next) { + let m = next.num; + let j = next.next; + some({num: n * 10u + m, next: j}) + } + }; + } + fn parse_conversion(s: str, i: uint, lim: uint, error: error_fn) -> + {piece: piece, next: uint} { + let parm = parse_parameter(s, i, lim); + let flags = parse_flags(s, parm.next, lim); + let width = parse_count(s, flags.next, lim); + let prec = parse_precision(s, width.next, lim); + let ty = parse_type(s, prec.next, lim, error); + ret {piece: + piece_conv({param: parm.param, + flags: flags.flags, + width: width.count, + precision: prec.count, + ty: ty.ty}), + next: ty.next}; + } + fn parse_parameter(s: str, i: uint, lim: uint) -> + {param: option::t<int>, next: uint} { + if i >= lim { ret {param: none, next: i}; } + let num = peek_num(s, i, lim); + ret alt num { + none. { {param: none, next: i} } + some(t) { + let n = t.num; + let j = t.next; + if j < lim && s[j] == '$' as u8 { + {param: some(n as int), next: j + 1u} + } else { {param: none, next: i} } + } + }; + } + fn parse_flags(s: str, i: uint, lim: uint) -> + {flags: [flag], next: uint} { + let noflags: [flag] = []; + if i >= lim { ret {flags: noflags, next: i}; } + + // FIXME: This recursion generates illegal instructions if the return + // value isn't boxed. Only started happening after the ivec conversion + fn more_(f: flag, s: str, i: uint, lim: uint) -> + @{flags: [flag], next: uint} { + let next = parse_flags(s, i + 1u, lim); + let rest = next.flags; + let j = next.next; + let curr: [flag] = [f]; + ret @{flags: curr + rest, next: j}; + } + let more = bind more_(_, s, i, lim); + let f = s[i]; + ret if f == '-' as u8 { + *more(flag_left_justify) + } else if f == '0' as u8 { + *more(flag_left_zero_pad) + } else if f == ' ' as u8 { + *more(flag_space_for_sign) + } else if f == '+' as u8 { + *more(flag_sign_always) + } else if f == '#' as u8 { + *more(flag_alternate) + } else { {flags: noflags, next: i} }; + } + fn parse_count(s: str, i: uint, lim: uint) -> {count: count, next: uint} { + ret if i >= lim { + {count: count_implied, next: i} + } else if s[i] == '*' as u8 { + let param = parse_parameter(s, i + 1u, lim); + let j = param.next; + alt param.param { + none. { {count: count_is_next_param, next: j} } + some(n) { {count: count_is_param(n), next: j} } + } + } else { + let num = peek_num(s, i, lim); + alt num { + none. { {count: count_implied, next: i} } + some(num) { + {count: count_is(num.num as int), next: num.next} + } + } + }; + } + fn parse_precision(s: str, i: uint, lim: uint) -> + {count: count, next: uint} { + ret if i >= lim { + {count: count_implied, next: i} + } else if s[i] == '.' as u8 { + let count = parse_count(s, i + 1u, lim); + + + // If there were no digits specified, i.e. the precision + // was ".", then the precision is 0 + alt count.count { + count_implied. { {count: count_is(0), next: count.next} } + _ { count } + } + } else { {count: count_implied, next: i} }; + } + fn parse_type(s: str, i: uint, lim: uint, error: error_fn) -> + {ty: ty, next: uint} { + if i >= lim { error("missing type in conversion"); } + let tstr = str::substr(s, i, 1u); + // TODO: Do we really want two signed types here? + // How important is it to be printf compatible? + let t = + if str::eq(tstr, "b") { + ty_bool + } else if str::eq(tstr, "s") { + ty_str + } else if str::eq(tstr, "c") { + ty_char + } else if str::eq(tstr, "d") || str::eq(tstr, "i") { + ty_int(signed) + } else if str::eq(tstr, "u") { + ty_int(unsigned) + } else if str::eq(tstr, "x") { + ty_hex(case_lower) + } else if str::eq(tstr, "X") { + ty_hex(case_upper) + } else if str::eq(tstr, "t") { + ty_bits + } else if str::eq(tstr, "o") { + ty_octal + } else if str::eq(tstr, "f") { + ty_float + } else { error("unknown type in conversion: " + tstr) }; + ret {ty: t, next: i + 1u}; + } +} + + +// Functions used by the fmt extension at runtime. For now there are a lot of +// decisions made a runtime. If it proves worthwhile then some of these +// conditions can be evaluated at compile-time. For now though it's cleaner to +// implement it this way, I think. +mod rt { + tag flag { + flag_left_justify; + flag_left_zero_pad; + flag_space_for_sign; + flag_sign_always; + flag_alternate; + + + // FIXME: This is a hack to avoid creating 0-length vec exprs, + // which have some difficulty typechecking currently. See + // comments in front::extfmt::make_flags + flag_none; + } + tag count { count_is(int); count_implied; } + tag ty { ty_default; ty_bits; ty_hex_upper; ty_hex_lower; ty_octal; } + + // FIXME: May not want to use a vector here for flags; + // instead just use a bool per flag + type conv = {flags: [flag], width: count, precision: count, ty: ty}; + + fn conv_int(cv: conv, i: int) -> str { + let radix = 10u; + let prec = get_int_precision(cv); + let s = int_to_str_prec(i, radix, prec); + if 0 <= i { + if have_flag(cv.flags, flag_sign_always) { + s = "+" + s; + } else if have_flag(cv.flags, flag_space_for_sign) { + s = " " + s; + } + } + ret pad(cv, s, pad_signed); + } + fn conv_uint(cv: conv, u: uint) -> str { + let prec = get_int_precision(cv); + let rs = + alt cv.ty { + ty_default. { uint_to_str_prec(u, 10u, prec) } + ty_hex_lower. { uint_to_str_prec(u, 16u, prec) } + ty_hex_upper. { str::to_upper(uint_to_str_prec(u, 16u, prec)) } + ty_bits. { uint_to_str_prec(u, 2u, prec) } + ty_octal. { uint_to_str_prec(u, 8u, prec) } + }; + ret pad(cv, rs, pad_unsigned); + } + fn conv_bool(cv: conv, b: bool) -> str { + let s = if b { "true" } else { "false" }; + // run the boolean conversion through the string conversion logic, + // giving it the same rules for precision, etc. + + ret conv_str(cv, s); + } + fn conv_char(cv: conv, c: char) -> str { + ret pad(cv, str::from_char(c), pad_nozero); + } + fn conv_str(cv: conv, s: str) -> str { + // For strings, precision is the maximum characters + // displayed + + // FIXME: substr works on bytes, not chars! + let unpadded = + alt cv.precision { + count_implied. { s } + count_is(max) { + if max as uint < str::char_len(s) { + str::substr(s, 0u, max as uint) + } else { s } + } + }; + ret pad(cv, unpadded, pad_nozero); + } + fn conv_float(cv: conv, f: float) -> str { + let (to_str, digits) = alt cv.precision { + count_is(c) { (float::to_str_exact, c as uint) } + count_implied. { (float::to_str, 6u) } + }; + let s = to_str(f, digits); + if 0.0 <= f { + if have_flag(cv.flags, flag_sign_always) { + s = "+" + s; + } else if have_flag(cv.flags, flag_space_for_sign) { + s = " " + s; + } + } + ret pad(cv, s, pad_signed); + } + + // Convert an int to string with minimum number of digits. If precision is + // 0 and num is 0 then the result is the empty string. + fn int_to_str_prec(num: int, radix: uint, prec: uint) -> str { + ret if num < 0 { + "-" + uint_to_str_prec(-num as uint, radix, prec) + } else { uint_to_str_prec(num as uint, radix, prec) }; + } + + // Convert a uint to string with a minimum number of digits. If precision + // is 0 and num is 0 then the result is the empty string. Could move this + // to uint: but it doesn't seem all that useful. + fn uint_to_str_prec(num: uint, radix: uint, prec: uint) -> str { + ret if prec == 0u && num == 0u { + "" + } else { + let s = uint::to_str(num, radix); + let len = str::char_len(s); + if len < prec { + let diff = prec - len; + let pad = str_init_elt('0', diff); + pad + s + } else { s } + }; + } + fn get_int_precision(cv: conv) -> uint { + ret alt cv.precision { + count_is(c) { c as uint } + count_implied. { 1u } + }; + } + + // FIXME: This might be useful in str: but needs to be utf8 safe first + fn str_init_elt(c: char, n_elts: uint) -> str { + let svec = vec::init_elt::<u8>(c as u8, n_elts); + + ret str::unsafe_from_bytes(svec); + } + tag pad_mode { pad_signed; pad_unsigned; pad_nozero; } + fn pad(cv: conv, s: str, mode: pad_mode) -> str { + let uwidth; + alt cv.width { + count_implied. { ret s; } + count_is(width) { + // FIXME: Maybe width should be uint + + uwidth = width as uint; + } + } + let strlen = str::char_len(s); + if uwidth <= strlen { ret s; } + let padchar = ' '; + let diff = uwidth - strlen; + if have_flag(cv.flags, flag_left_justify) { + let padstr = str_init_elt(padchar, diff); + ret s + padstr; + } + let might_zero_pad = false; + let signed = false; + alt mode { + pad_nozero. { + // fallthrough + + } + pad_signed. { might_zero_pad = true; signed = true; } + pad_unsigned. { might_zero_pad = true; } + } + fn have_precision(cv: conv) -> bool { + ret alt cv.precision { count_implied. { false } _ { true } }; + } + let zero_padding = false; + if might_zero_pad && have_flag(cv.flags, flag_left_zero_pad) && + !have_precision(cv) { + padchar = '0'; + zero_padding = true; + } + let padstr = str_init_elt(padchar, diff); + // This is completely heinous. If we have a signed value then + // potentially rip apart the intermediate result and insert some + // zeros. It may make sense to convert zero padding to a precision + // instead. + + if signed && zero_padding && str::byte_len(s) > 0u { + let head = s[0]; + if head == '+' as u8 || head == '-' as u8 || head == ' ' as u8 { + let headstr = str::unsafe_from_bytes([head]); + let bytelen = str::byte_len(s); + let numpart = str::substr(s, 1u, bytelen - 1u); + ret headstr + padstr + numpart; + } + } + ret padstr + s; + } + fn have_flag(flags: [flag], f: flag) -> bool { + for candidate: flag in flags { if candidate == f { ret true; } } + ret false; + } +} +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/float.rs b/src/libstd/float.rs new file mode 100644 index 00000000000..f916e97bbf8 --- /dev/null +++ b/src/libstd/float.rs @@ -0,0 +1,350 @@ +/* +Module: float +*/ + +/** + * Section: String Conversions + */ + +/* +Function: to_str_common + +Converts a float to a string + +Parameters: + +num - The float value +digits - The number of significant digits +exact - Whether to enforce the exact number of significant digits +*/ +fn to_str_common(num: float, digits: uint, exact: bool) -> str { + let (num, accum) = num < 0.0 ? (-num, "-") : (num, ""); + let trunc = num as uint; + let frac = num - (trunc as float); + accum += uint::str(trunc); + if frac == 0.0 || digits == 0u { ret accum; } + accum += "."; + let i = digits; + let epsilon = 1. / pow_uint_to_uint_as_float(10u, i); + while i > 0u && (frac >= epsilon || exact) { + frac *= 10.0; + epsilon *= 10.0; + let digit = frac as uint; + accum += uint::str(digit); + frac -= digit as float; + i -= 1u; + } + ret accum; + +} + +/* +Function: to_str + +Converts a float to a string with exactly the number of provided significant +digits + +Parameters: + +num - The float value +digits - The number of significant digits +*/ +fn to_str_exact(num: float, digits: uint) -> str { + to_str_common(num, digits, true) +} + +/* +Function: to_str + +Converts a float to a string with a maximum number of significant digits + +Parameters: + +num - The float value +digits - The number of significant digits +*/ +fn to_str(num: float, digits: uint) -> str { + to_str_common(num, digits, false) +} + +/* +Function: from_str + +Convert a string to a float + +This function accepts strings such as +* "3.14" +* "+3.14", equivalent to "3.14" +* "-3.14" +* "2.5E10", or equivalently, "2.5e10" +* "2.5E-10" +* "", or, equivalently, "." (understood as 0) +* "5." +* ".5", or, equivalently, "0.5" + +Leading and trailing whitespace are ignored. + +Parameters: + +num - A string, possibly empty. + +Returns: + +<NaN> If the string did not represent a valid number. +Otherwise, the floating-point number represented [num]. +*/ +fn from_str(num: str) -> float { + let num = str::trim(num); + + let pos = 0u; //Current byte position in the string. + //Used to walk the string in O(n). + let len = str::byte_len(num); //Length of the string, in bytes. + + if len == 0u { ret 0.; } + let total = 0f; //Accumulated result + let c = 'z'; //Latest char. + + //The string must start with one of the following characters. + alt str::char_at(num, 0u) { + '-' | '+' | '0' to '9' | '.' {} + _ { ret NaN; } + } + + //Determine if first char is '-'/'+'. Set [pos] and [neg] accordingly. + let neg = false; //Sign of the result + alt str::char_at(num, 0u) { + '-' { + neg = true; + pos = 1u; + } + '+' { + pos = 1u; + } + _ {} + } + + //Examine the following chars until '.', 'e', 'E' + while(pos < len) { + let char_range = str::char_range_at(num, pos); + c = char_range.ch; + pos = char_range.next; + alt c { + '0' to '9' { + total = total * 10f; + total += ((c as int) - ('0' as int)) as float; + } + '.' | 'e' | 'E' { + break; + } + _ { + ret NaN; + } + } + } + + if c == '.' {//Examine decimal part + let decimal = 1.f; + while(pos < len) { + let char_range = str::char_range_at(num, pos); + c = char_range.ch; + pos = char_range.next; + alt c { + '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' { + decimal /= 10.f; + total += (((c as int) - ('0' as int)) as float)*decimal; + } + 'e' | 'E' { + break; + } + _ { + ret NaN; + } + } + } + } + + if (c == 'e') | (c == 'E') {//Examine exponent + let exponent = 0u; + let neg_exponent = false; + if(pos < len) { + let char_range = str::char_range_at(num, pos); + c = char_range.ch; + alt c { + '+' { + pos = char_range.next; + } + '-' { + pos = char_range.next; + neg_exponent = true; + } + _ {} + } + while(pos < len) { + let char_range = str::char_range_at(num, pos); + c = char_range.ch; + alt c { + '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' { + exponent *= 10u; + exponent += ((c as uint) - ('0' as uint)); + } + _ { + break; + } + } + pos = char_range.next; + } + let multiplier = pow_uint_to_uint_as_float(10u, exponent); + //Note: not [int::pow], otherwise, we'll quickly + //end up with a nice overflow + if neg_exponent { + total = total / multiplier; + } else { + total = total * multiplier; + } + } else { + ret NaN; + } + } + + if(pos < len) { + ret NaN; + } else { + if(neg) { + total *= -1f; + } + ret total; + } +} + +/** + * Section: Arithmetics + */ + +/* +Function: pow_uint_to_uint_as_float + +Compute the exponentiation of an integer by another integer as a float. + +Parameters: +x - The base. +pow - The exponent. + +Returns: +<NaN> of both `x` and `pow` are `0u`, otherwise `x^pow`. +*/ +fn pow_uint_to_uint_as_float(x: uint, pow: uint) -> float { + if x == 0u { + if pow == 0u { + ret NaN; + } + ret 0.; + } + let my_pow = pow; + let total = 1f; + let multiplier = x as float; + while (my_pow > 0u) { + if my_pow % 2u == 1u { + total = total * multiplier; + } + my_pow /= 2u; + multiplier *= multiplier; + } + ret total; +} + + +/** + * Section: Constants + */ + +//TODO: Once this is possible, replace the body of these functions +//by an actual constant. + +/* Const: NaN */ +const NaN: float = 0./0.; + +/* Predicate: isNaN */ +pure fn isNaN(f: float) -> bool { f != f } + +/* Const: infinity */ +const infinity: float = 1./0.; + +/* Const: neg_infinity */ +const neg_infinity: float = -1./0.; + +/* Function: add */ +pure fn add(x: float, y: float) -> float { ret x + y; } + +/* Function: sub */ +pure fn sub(x: float, y: float) -> float { ret x - y; } + +/* Function: mul */ +pure fn mul(x: float, y: float) -> float { ret x * y; } + +/* Function: div */ +pure fn div(x: float, y: float) -> float { ret x / y; } + +/* Function: rem */ +pure fn rem(x: float, y: float) -> float { ret x % y; } + +/* Predicate: lt */ +pure fn lt(x: float, y: float) -> bool { ret x < y; } + +/* Predicate: le */ +pure fn le(x: float, y: float) -> bool { ret x <= y; } + +/* Predicate: eq */ +pure fn eq(x: float, y: float) -> bool { ret x == y; } + +/* Predicate: ne */ +pure fn ne(x: float, y: float) -> bool { ret x != y; } + +/* Predicate: ge */ +pure fn ge(x: float, y: float) -> bool { ret x >= y; } + +/* Predicate: gt */ +pure fn gt(x: float, y: float) -> bool { ret x > y; } + +/* +Predicate: positive + +Returns true if `x` is a positive number, including +0.0 and +Infinity. + */ +pure fn positive(x: float) -> bool { ret x > 0. || (1./x) == infinity; } + +/* +Predicate: negative + +Returns true if `x` is a negative number, including -0.0 and -Infinity. + */ +pure fn negative(x: float) -> bool { ret x < 0. || (1./x) == neg_infinity; } + +/* +Predicate: nonpositive + +Returns true if `x` is a negative number, including -0.0 and -Infinity. +(This is the same as `float::negative`.) +*/ +pure fn nonpositive(x: float) -> bool { + ret x < 0. || (1./x) == neg_infinity; +} + +/* +Predicate: nonnegative + +Returns true if `x` is a positive number, including +0.0 and +Infinity. +(This is the same as `float::positive`.) +*/ +pure fn nonnegative(x: float) -> bool { + ret x > 0. || (1./x) == infinity; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/libstd/four.rs b/src/libstd/four.rs new file mode 100644 index 00000000000..11c42fb7018 --- /dev/null +++ b/src/libstd/four.rs @@ -0,0 +1,229 @@ +// -*- rust -*- + +/* +Module: four + +The fourrternary Belnap relevance logic FOUR represented as ADT + +This allows reasoning with four logic values (true, false, none, both). + +Implementation: Truth values are represented using a single u8 and +all operations are done using bit operations which is fast +on current cpus. +*/ + +import tri; + +export t, none, true, false, both; +export not, and, or, xor, implies, implies_materially; +export eq, ne, is_true, is_false; +export from_str, to_str, all_values, to_trit, to_bit; + +/* +Type: t + +The type of fourrternary logic values + +It may be thought of as tuple `(y, x)` of two bools + +*/ +type t = u8; + +const b0: u8 = 1u8; +const b1: u8 = 2u8; +const b01: u8 = 3u8; + +/* +Constant: none + +Logic value `(0, 0)` for bottom (neither true or false) +*/ +const none: t = 0u8; + +/* +Constant: true + +Logic value `(0, 1)` for truth +*/ +const true: t = 1u8; + +/* +Constant: false + +Logic value `(1, 0)` for falsehood +*/ +const false: t = 2u8; + +/* +Constant: both + +Logic value `(1, 1)` for top (both true and false) +*/ +const both: t = 3u8; + +/* Function: not + +Negation/Inverse + +Returns: + +`'(v.y, v.x)` +*/ +pure fn not(v: t) -> t { ((v << 1u8) | (v >> 1u8)) & b01 } + +/* Function: and + +Conjunction + +Returns: + +`(a.x | b.x, a.y & b.y)` +*/ +pure fn and(a: t, b: t) -> t { ((a & b) & b0) | ((a | b) & b1) } + +/* Function: or + +Disjunction + +Returns: + +`(a.x & b.x, a.y | b.y)` +*/ +pure fn or(a: t, b: t) -> t { ((a | b) & b0) | ((a & b) & b1) } + +/* Function: xor + +Classic exclusive or + +Returns: + +`or(and(a, not(b)), and(not(a), b))` +*/ +pure fn xor(a: t, b: t) -> t { or(and(a, not(b)), and(not(a), b)) } + +/* +Function: implies + +Strong implication (from `a` strongly follows `b`) + +Returns: + +`( x1 & y2, !x1 | x2)` +*/ +pure fn implies(a: t, b: t) -> t { ((a << 1u8) & b & b1) | (((!a) | b) & b0) } + +/* +Function: implies_materially + +Classic (material) implication in the logic +(from `a` materially follows `b`) + +Returns: + +`or(not(a), b)` +*/ +pure fn implies_materially(a: t, b: t) -> t { or(not(a), b) } + +/* +Predicate: eq + +Returns: + +true if truth values `a` and `b` are indistinguishable in the logic +*/ +pure fn eq(a: t, b: t) -> bool { a == b } + +/* +Predicate: ne + +Returns: + +true if truth values `a` and `b` are distinguishable in the logic +*/ +pure fn ne(a: t, b: t) -> bool { a != b } + +/* +Predicate: is_true + +Returns: + +true if `v` represents truth in the logic (is `true` or `both`) +*/ +pure fn is_true(v: t) -> bool { (v & b0) != 0u8 } + +/* +Predicate: is_false + +Returns: + +true if `v` represents falsehood in the logic (is `false` or `none`) +*/ +pure fn is_false(v: t) -> bool { (v & b0) == 0u8 } + +/* +Function: from_str + +Parse logic value from `s` +*/ +pure fn from_str(s: str) -> t { + alt s { + "none" { none } + "false" { four::false } + "true" { four::true } + "both" { both } + } +} + +/* +Function: to_str + +Convert `v` into a string +*/ +pure fn to_str(v: t) -> str { + // FIXME replace with consts as soon as that works + alt v { + 0u8 { "none" } + 1u8 { "true" } + 2u8 { "false" } + 3u8 { "both" } + } +} + +/* +Function: all_values + +Iterates over all truth values by passing them to `blk` +in an unspecified order +*/ +fn all_values(blk: block(v: t)) { + blk(both); + blk(four::true); + blk(four::false); + blk(none); +} + +/* +Function: to_bit + +Returns: + +An u8 whose first bit is set if `if_true(v)` holds +*/ +fn to_bit(v: t) -> u8 { v & b0 } + +/* +Function: to_tri + +Returns: + +A trit of `v` (`both` and `none` are both coalesced into `trit::unknown`) +*/ +fn to_trit(v: t) -> tri::t { v & (v ^ not(v)) } + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs new file mode 100644 index 00000000000..fcd95040ab0 --- /dev/null +++ b/src/libstd/fs.rs @@ -0,0 +1,391 @@ +/* +Module: fs + +File system manipulation +*/ + +import os; +import os::getcwd; +import os_fs; + +#[abi = "cdecl"] +native mod rustrt { + fn rust_file_is_dir(path: str::sbuf) -> int; +} + +/* +Function: path_sep + +Get the default path separator for the host platform +*/ +fn path_sep() -> str { ret str::from_char(os_fs::path_sep); } + +// FIXME: This type should probably be constrained +/* +Type: path + +A path or fragment of a filesystem path +*/ +type path = str; + +/* +Function: dirname + +Get the directory portion of a path + +Returns all of the path up to, but excluding, the final path separator. +The dirname of "/usr/share" will be "/usr", but the dirname of +"/usr/share/" is "/usr/share". + +If the path is not prefixed with a directory, then "." is returned. +*/ +fn dirname(p: path) -> path { + let i: int = str::rindex(p, os_fs::path_sep as u8); + if i == -1 { + i = str::rindex(p, os_fs::alt_path_sep as u8); + if i == -1 { ret "."; } + } + ret str::substr(p, 0u, i as uint); +} + +/* +Function: basename + +Get the file name portion of a path + +Returns the portion of the path after the final path separator. +The basename of "/usr/share" will be "share". If there are no +path separators in the path then the returned path is identical to +the provided path. If an empty path is provided or the path ends +with a path separator then an empty path is returned. +*/ +fn basename(p: path) -> path { + let i: int = str::rindex(p, os_fs::path_sep as u8); + if i == -1 { + i = str::rindex(p, os_fs::alt_path_sep as u8); + if i == -1 { ret p; } + } + let len = str::byte_len(p); + if i + 1 as uint >= len { ret p; } + ret str::slice(p, i + 1 as uint, len); +} + + +// FIXME: Need some typestate to avoid bounds check when len(pre) == 0 +/* +Function: connect + +Connects to path segments + +Given paths `pre` and `post` this function will return a path +that is equal to `post` appended to `pre`, inserting a path separator +between the two as needed. +*/ +fn connect(pre: path, post: path) -> path { + let len = str::byte_len(pre); + ret if pre[len - 1u] == os_fs::path_sep as u8 { + + // Trailing '/'? + pre + post + } else { pre + path_sep() + post }; +} + +/* +Function: connect_many + +Connects a vector of path segments into a single path. + +Inserts path separators as needed. +*/ +fn connect_many(paths: [path]) : vec::is_not_empty(paths) -> path { + ret if vec::len(paths) == 1u { + paths[0] + } else { + let rest = vec::slice(paths, 1u, vec::len(paths)); + check vec::is_not_empty(rest); + connect(paths[0], connect_many(rest)) + } +} + +/* +Function: file_id_dir + +Indicates whether a path represents a directory. +*/ +fn file_is_dir(p: path) -> bool { + ret str::as_buf(p, {|buf| rustrt::rust_file_is_dir(buf) != 0 }); +} + +/* +Function: make_dir + +Creates a directory at the specified path. +*/ +fn make_dir(p: path, mode: ctypes::c_int) -> bool { + ret mkdir(p, mode); + + #[cfg(target_os = "win32")] + fn mkdir(_p: path, _mode: ctypes::c_int) -> bool unsafe { + // FIXME: turn mode into something useful? + ret str::as_buf(_p, {|buf| + os::kernel32::CreateDirectoryA( + buf, unsafe::reinterpret_cast(0)) + }); + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] + fn mkdir(_p: path, _mode: ctypes::c_int) -> bool { + ret str::as_buf(_p, {|buf| os::libc::mkdir(buf, _mode) == 0i32 }); + } +} + +/* +Function: list_dir + +Lists the contents of a directory. +*/ +fn list_dir(p: path) -> [str] { + let p = p; + let pl = str::byte_len(p); + if pl == 0u || p[pl - 1u] as char != os_fs::path_sep { p += path_sep(); } + let full_paths: [str] = []; + for filename: str in os_fs::list_dir(p) { + if !str::eq(filename, ".") { + if !str::eq(filename, "..") { full_paths += [p + filename]; } + } + } + ret full_paths; +} + +/* +Function: remove_dir + +Removes a directory at the specified path. +*/ +fn remove_dir(p: path) -> bool { + ret rmdir(p); + + #[cfg(target_os = "win32")] + fn rmdir(_p: path) -> bool { + ret str::as_buf(_p, {|buf| os::kernel32::RemoveDirectoryA(buf)}); + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] + fn rmdir(_p: path) -> bool { + ret str::as_buf(_p, {|buf| os::libc::rmdir(buf) == 0i32 }); + } +} + +fn change_dir(p: path) -> bool { + ret chdir(p); + + #[cfg(target_os = "win32")] + fn chdir(_p: path) -> bool { + ret str::as_buf(_p, {|buf| os::kernel32::SetCurrentDirectoryA(buf)}); + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] + fn chdir(_p: path) -> bool { + ret str::as_buf(_p, {|buf| os::libc::chdir(buf) == 0i32 }); + } +} + +/* +Function: path_is_absolute + +Indicates whether a path is absolute. + +A path is considered absolute if it begins at the filesystem root ("/") or, +on Windows, begins with a drive letter. +*/ +fn path_is_absolute(p: path) -> bool { ret os_fs::path_is_absolute(p); } + +// FIXME: under Windows, we should prepend the current drive letter to paths +// that start with a slash. +/* +Function: make_absolute + +Convert a relative path to an absolute path + +If the given path is relative, return it prepended with the current working +directory. If the given path is already an absolute path, return it +as is. +*/ +fn make_absolute(p: path) -> path { + if path_is_absolute(p) { ret p; } else { ret connect(getcwd(), p); } +} + +/* +Function: split + +Split a path into it's individual components + +Splits a given path by path separators and returns a vector containing +each piece of the path. On Windows, if the path is absolute then +the first element of the returned vector will be the drive letter +followed by a colon. +*/ +fn split(p: path) -> [path] { + let split1 = str::split(p, os_fs::path_sep as u8); + let split2 = []; + for s in split1 { + split2 += str::split(s, os_fs::alt_path_sep as u8); + } + ret split2; +} + +/* +Function: splitext + +Split a path into a pair of strings with the first element being the filename +without the extension and the second being either empty or the file extension +including the period. Leading periods in the basename are ignored. If the +path includes directory components then they are included in the filename part +of the result pair. +*/ +fn splitext(p: path) -> (str, str) { + if str::is_empty(p) { ("", "") } + else { + let parts = str::split(p, '.' as u8); + if vec::len(parts) > 1u { + let base = str::connect(vec::init(parts), "."); + let ext = "." + option::get(vec::last(parts)); + + fn is_dotfile(base: str) -> bool { + str::is_empty(base) + || str::ends_with( + base, str::from_char(os_fs::path_sep)) + || str::ends_with( + base, str::from_char(os_fs::alt_path_sep)) + } + + fn ext_contains_sep(ext: str) -> bool { + vec::len(split(ext)) > 1u + } + + fn no_basename(ext: str) -> bool { + str::ends_with( + ext, str::from_char(os_fs::path_sep)) + || str::ends_with( + ext, str::from_char(os_fs::alt_path_sep)) + } + + if is_dotfile(base) + || ext_contains_sep(ext) + || no_basename(ext) { + (p, "") + } else { + (base, ext) + } + } else { + (p, "") + } + } +} + +/* +Function: normalize + +Removes extra "." and ".." entries from paths. + +Does not follow symbolic links. +*/ +fn normalize(p: path) -> path { + let s = split(p); + let s = strip_dots(s); + let s = rollup_doubledots(s); + + let s = if check vec::is_not_empty(s) { + connect_many(s) + } else { + "" + }; + let s = reabsolute(p, s); + let s = reterminate(p, s); + + let s = if str::byte_len(s) == 0u { + "." + } else { + s + }; + + ret s; + + fn strip_dots(s: [path]) -> [path] { + vec::filter_map({ |elem| + if elem == "." { + option::none + } else { + option::some(elem) + } + }, s) + } + + fn rollup_doubledots(s: [path]) -> [path] { + if vec::is_empty(s) { + ret []; + } + + let t = []; + let i = vec::len(s); + let skip = 0; + do { + i -= 1u; + if s[i] == ".." { + skip += 1; + } else { + if skip == 0 { + t += [s[i]]; + } else { + skip -= 1; + } + } + } while i != 0u; + let t = vec::reversed(t); + while skip > 0 { + t += [".."]; + skip -= 1; + } + ret t; + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] + fn reabsolute(orig: path, new: path) -> path { + if path_is_absolute(orig) { + path_sep() + new + } else { + new + } + } + + #[cfg(target_os = "win32")] + fn reabsolute(orig: path, new: path) -> path { + if path_is_absolute(orig) && orig[0] == os_fs::path_sep as u8 { + str::from_char(os_fs::path_sep) + new + } else { + new + } + } + + fn reterminate(orig: path, new: path) -> path { + let last = orig[str::byte_len(orig) - 1u]; + if last == os_fs::path_sep as u8 + || last == os_fs::path_sep as u8 { + ret new + path_sep(); + } else { + ret new; + } + } +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/fun_treemap.rs b/src/libstd/fun_treemap.rs new file mode 100644 index 00000000000..1f4e6b9491a --- /dev/null +++ b/src/libstd/fun_treemap.rs @@ -0,0 +1,98 @@ +/* +Module: fun_treemap + +A functional key,value store that works on anything. + +This works using a binary search tree. In the first version, it's a +very naive algorithm, but it will probably be updated to be a +red-black tree or something else. + +This is copied and modified from treemap right now. It's missing a lot +of features. + +*/ + +import option::{some, none}; +import option = option::t; + +export treemap; +export init; +export insert; +export find; +export traverse; + +/* Section: Types */ + +/* +Type: treemap +*/ +type treemap<K, V> = @tree_node<K, V>; + +/* +Tag: tree_node +*/ +tag tree_node<K, V> { + empty; + node(@K, @V, @tree_node<K, V>, @tree_node<K, V>); +} + +/* Section: Operations */ + +/* +Function: init + +Create a treemap +*/ +fn init<K, V>() -> treemap<K, V> { @empty } + +/* +Function: insert + +Insert a value into the map +*/ +fn insert<copy K, copy V>(m: treemap<K, V>, k: K, v: V) -> treemap<K, V> { + @alt m { + @empty. { node(@k, @v, @empty, @empty) } + @node(@kk, vv, left, right) { + if k < kk { + node(@kk, vv, insert(left, k, v), right) + } else if k == kk { + node(@kk, @v, left, right) + } else { node(@kk, vv, left, insert(right, k, v)) } + } + } +} + +/* +Function: find + +Find a value based on the key +*/ +fn find<K, copy V>(m: treemap<K, V>, k: K) -> option<V> { + alt *m { + empty. { none } + node(@kk, @v, left, right) { + if k == kk { + some(v) + } else if k < kk { find(left, k) } else { find(right, k) } + } + } +} + +/* +Function: traverse + +Visit all pairs in the map in order. +*/ +fn traverse<K, copy V>(m: treemap<K, V>, f: block(K, V)) { + alt *m { + empty. { } + node(@k, @v, _, _) { + // copy v to make aliases work out + let v1 = v; + alt *m { node(_, _, left, _) { traverse(left, f); } } + f(k, v1); + alt *m { node(_, _, _, right) { traverse(right, f); } } + } + } +} diff --git a/src/libstd/generic_os.rs b/src/libstd/generic_os.rs new file mode 100644 index 00000000000..bbbd76f8217 --- /dev/null +++ b/src/libstd/generic_os.rs @@ -0,0 +1,99 @@ +/* +Module: generic_os + +Some miscellaneous platform functions. + +These should be rolled into another module. +*/ + + +// Wow, this is an ugly way to write doc comments + +#[cfg(bogus)] +/* +Function: getenv + +Get the value of an environment variable +*/ +fn getenv(n: str) -> option::t<str> { } + +#[cfg(bogus)] +/* +Function: setenv + +Set the value of an environment variable +*/ +fn setenv(n: str, v: str) { } + +#[cfg(target_os = "linux")] +#[cfg(target_os = "macos")] +fn getenv(n: str) -> option::t<str> unsafe { + let s = str::as_buf(n, {|buf| os::libc::getenv(buf) }); + ret if unsafe::reinterpret_cast(s) == 0 { + option::none::<str> + } else { + let s = unsafe::reinterpret_cast(s); + option::some::<str>(str::str_from_cstr(s)) + }; +} + +#[cfg(target_os = "linux")] +#[cfg(target_os = "macos")] +fn setenv(n: str, v: str) { + // FIXME (868) + str::as_buf( + n, + // FIXME (868) + {|nbuf| + str::as_buf( + v, + {|vbuf| + os::libc::setenv(nbuf, vbuf, 1i32)})}); +} + +#[cfg(target_os = "win32")] +fn getenv(n: str) -> option::t<str> { + let nsize = 256u; + while true { + let v: [u8] = []; + vec::reserve(v, nsize); + let res = + str::as_buf(n, + {|nbuf| + unsafe { + let vbuf = vec::to_ptr(v); + os::kernel32::GetEnvironmentVariableA(nbuf, vbuf, + nsize) + } + }); + if res == 0u { + ret option::none; + } else if res < nsize { + unsafe { + vec::unsafe::set_len(v, res); + } + ret option::some(str::unsafe_from_bytes(v)); + } else { nsize = res; } + } + fail; +} + +#[cfg(target_os = "win32")] +fn setenv(n: str, v: str) { + // FIXME (868) + let _: () = + str::as_buf(n, {|nbuf| + let _: () = + str::as_buf(v, {|vbuf| + os::kernel32::SetEnvironmentVariableA(nbuf, vbuf); + }); + }); +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/getopts.rs b/src/libstd/getopts.rs new file mode 100644 index 00000000000..438bec0a90b --- /dev/null +++ b/src/libstd/getopts.rs @@ -0,0 +1,386 @@ +/* +Module: getopts + +Simple getopt alternative. Construct a vector of options, either by using +reqopt, optopt, and optflag or by building them from components yourself, and +pass them to getopts, along with a vector of actual arguments (not including +argv[0]). You'll either get a failure code back, or a match. You'll have to +verify whether the amount of 'free' arguments in the match is what you +expect. Use opt_* accessors to get argument values out of the match object. + +Single-character options are expected to appear on the command line with a +single preceeding dash; multiple-character options are expected to be +proceeded by two dashes. Options that expect an argument accept their argument +following either a space or an equals sign. + +Example: + +The following example shows simple command line parsing for an application +that requires an input file to be specified, accepts an optional output file +name following -o, and accepts both -h and --help as optional flags. + +> fn main(args: [str]) { +> let opts = [ +> optopt("o"), +> optflag("h"), +> optflag("help") +> ]; +> let match = alt getopts(vec::shift(args), opts) { +> success(m) { m } +> failure(f) { fail fail_str(f) } +> }; +> if opt_present(match, "h") || opt_present(match, "help") { +> print_usage(); +> ret; +> } +> let output = opt_maybe_str(match, "o"); +> let input = if !vec::is_empty(match.free) { +> match.free[0] +> } else { +> print_usage(); +> ret; +> } +> do_work(input, output); +> } + +*/ + +import option::{some, none}; +export opt; +export reqopt; +export optopt; +export optflag; +export optflagopt; +export optmulti; +export getopts; +export result; +export success; +export failure; +export match; +export fail_; +export fail_str; +export opt_present; +export opt_str; +export opt_strs; +export opt_maybe_str; +export opt_default; + +tag name { long(str); short(char); } + +tag hasarg { yes; no; maybe; } + +tag occur { req; optional; multi; } + +/* +Type: opt + +A description of a possible option +*/ +type opt = {name: name, hasarg: hasarg, occur: occur}; + +fn mkname(nm: str) -> name { + ret if str::char_len(nm) == 1u { + short(str::char_at(nm, 0u)) + } else { long(nm) }; +} + +/* +Function: reqopt + +Create an option that is required and takes an argument +*/ +fn reqopt(name: str) -> opt { + ret {name: mkname(name), hasarg: yes, occur: req}; +} + +/* +Function: optopt + +Create an option that is optional and takes an argument +*/ +fn optopt(name: str) -> opt { + ret {name: mkname(name), hasarg: yes, occur: optional}; +} + +/* +Function: optflag + +Create an option that is optional and does not take an argument +*/ +fn optflag(name: str) -> opt { + ret {name: mkname(name), hasarg: no, occur: optional}; +} + +/* +Function: optflagopt + +Create an option that is optional and takes an optional argument +*/ +fn optflagopt(name: str) -> opt { + ret {name: mkname(name), hasarg: maybe, occur: optional}; +} + +/* +Function: optmulti + +Create an option that is optional, takes an argument, and may occur +multiple times +*/ +fn optmulti(name: str) -> opt { + ret {name: mkname(name), hasarg: yes, occur: multi}; +} + +tag optval { val(str); given; } + +/* +Type: match + +The result of checking command line arguments. Contains a vector +of matches and a vector of free strings. +*/ +type match = {opts: [opt], vals: [mutable [optval]], free: [str]}; + +fn is_arg(arg: str) -> bool { + ret str::byte_len(arg) > 1u && arg[0] == '-' as u8; +} + +fn name_str(nm: name) -> str { + ret alt nm { short(ch) { str::from_char(ch) } long(s) { s } }; +} + +fn find_opt(opts: [opt], nm: name) -> option::t<uint> { + let i = 0u; + let l = vec::len::<opt>(opts); + while i < l { if opts[i].name == nm { ret some::<uint>(i); } i += 1u; } + ret none::<uint>; +} + +/* +Type: fail_ + +The type returned when the command line does not conform to the +expected format. Pass this value to <fail_str> to get an error message. +*/ +tag fail_ { + argument_missing(str); + unrecognized_option(str); + option_missing(str); + option_duplicated(str); + unexpected_argument(str); +} + +/* +Function: fail_str + +Convert a <fail_> tag into an error string +*/ +fn fail_str(f: fail_) -> str { + ret alt f { + argument_missing(nm) { "Argument to option '" + nm + "' missing." } + unrecognized_option(nm) { "Unrecognized option: '" + nm + "'." } + option_missing(nm) { "Required option '" + nm + "' missing." } + option_duplicated(nm) { + "Option '" + nm + "' given more than once." + } + unexpected_argument(nm) { + "Option " + nm + " does not take an argument." + } + }; +} + +/* +Type: result + +The result of parsing a command line with a set of options + +Variants: + +success(match) - Returned from getopts on success +failure(fail_) - Returned from getopts on failure +*/ +tag result { success(match); failure(fail_); } + +/* +Function: getopts + +Parse command line arguments according to the provided options + +Returns: + +success(match) - On success. Use functions such as <opt_present> + <opt_str>, etc. to interrogate results. +failure(fail_) - On failure. Use <fail_str> to get an error message. +*/ +fn getopts(args: [str], opts: [opt]) -> result { + let n_opts = vec::len::<opt>(opts); + fn f(_x: uint) -> [optval] { ret []; } + let vals = vec::init_fn_mut::<[optval]>(f, n_opts); + let free: [str] = []; + let l = vec::len(args); + let i = 0u; + while i < l { + let cur = args[i]; + let curlen = str::byte_len(cur); + if !is_arg(cur) { + free += [cur]; + } else if str::eq(cur, "--") { + let j = i + 1u; + while j < l { free += [args[j]]; j += 1u; } + break; + } else { + let names; + let i_arg = option::none::<str>; + if cur[1] == '-' as u8 { + let tail = str::slice(cur, 2u, curlen); + let eq = str::index(tail, '=' as u8); + if eq == -1 { + names = [long(tail)]; + } else { + names = [long(str::slice(tail, 0u, eq as uint))]; + i_arg = + option::some::<str>(str::slice(tail, + (eq as uint) + 1u, + curlen - 2u)); + } + } else { + let j = 1u; + names = []; + while j < curlen { + let range = str::char_range_at(cur, j); + names += [short(range.ch)]; + j = range.next; + } + } + let name_pos = 0u; + for nm: name in names { + name_pos += 1u; + let optid; + alt find_opt(opts, nm) { + some(id) { optid = id; } + none. { ret failure(unrecognized_option(name_str(nm))); } + } + alt opts[optid].hasarg { + no. { + if !option::is_none::<str>(i_arg) { + ret failure(unexpected_argument(name_str(nm))); + } + vals[optid] += [given]; + } + maybe. { + if !option::is_none::<str>(i_arg) { + vals[optid] += [val(option::get(i_arg))]; + } else if name_pos < vec::len::<name>(names) || + i + 1u == l || is_arg(args[i + 1u]) { + vals[optid] += [given]; + } else { i += 1u; vals[optid] += [val(args[i])]; } + } + yes. { + if !option::is_none::<str>(i_arg) { + vals[optid] += [val(option::get::<str>(i_arg))]; + } else if i + 1u == l { + ret failure(argument_missing(name_str(nm))); + } else { i += 1u; vals[optid] += [val(args[i])]; } + } + } + } + } + i += 1u; + } + i = 0u; + while i < n_opts { + let n = vec::len::<optval>(vals[i]); + let occ = opts[i].occur; + if occ == req { + if n == 0u { + ret failure(option_missing(name_str(opts[i].name))); + } + } + if occ != multi { + if n > 1u { + ret failure(option_duplicated(name_str(opts[i].name))); + } + } + i += 1u; + } + ret success({opts: opts, vals: vals, free: free}); +} + +fn opt_vals(m: match, nm: str) -> [optval] { + ret alt find_opt(m.opts, mkname(nm)) { + some(id) { m.vals[id] } + none. { log_err "No option '" + nm + "' defined."; fail } + }; +} + +fn opt_val(m: match, nm: str) -> optval { ret opt_vals(m, nm)[0]; } + +/* +Function: opt_present + +Returns true if an option was matched +*/ +fn opt_present(m: match, nm: str) -> bool { + ret vec::len::<optval>(opt_vals(m, nm)) > 0u; +} + +/* +Function: opt_str + +Returns the string argument supplied to a matching option + +Failure: + +- If the option was not matched +- If the match did not take an argument +*/ +fn opt_str(m: match, nm: str) -> str { + ret alt opt_val(m, nm) { val(s) { s } _ { fail } }; +} + +/* +Function: opt_str + +Returns a vector of the arguments provided to all matches of the given option. +Used when an option accepts multiple values. +*/ +fn opt_strs(m: match, nm: str) -> [str] { + let acc: [str] = []; + for v: optval in opt_vals(m, nm) { + alt v { val(s) { acc += [s]; } _ { } } + } + ret acc; +} + +/* +Function: opt_str + +Returns the string argument supplied to a matching option or none +*/ +fn opt_maybe_str(m: match, nm: str) -> option::t<str> { + let vals = opt_vals(m, nm); + if vec::len::<optval>(vals) == 0u { ret none::<str>; } + ret alt vals[0] { val(s) { some::<str>(s) } _ { none::<str> } }; +} + + +/* +Function: opt_default + +Returns the matching string, a default, or none + +Returns none if the option was not present, `def` if the option was +present but no argument was provided, and the argument if the option was +present and an argument was provided. +*/ +fn opt_default(m: match, nm: str, def: str) -> option::t<str> { + let vals = opt_vals(m, nm); + if vec::len::<optval>(vals) == 0u { ret none::<str>; } + ret alt vals[0] { val(s) { some::<str>(s) } _ { some::<str>(def) } } +} +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/int.rs b/src/libstd/int.rs new file mode 100644 index 00000000000..2ab299800ad --- /dev/null +++ b/src/libstd/int.rs @@ -0,0 +1,189 @@ +/* +Module: int +*/ + +/* +Const: max_value + +The maximum value of an integer +*/ +// FIXME: Find another way to access the machine word size in a const expr +#[cfg(target_arch="x86")] +const max_value: int = (-1 << 31)-1; + +#[cfg(target_arch="x86_64")] +const max_value: int = (-1 << 63)-1; + +/* +Const: min_value + +The minumum value of an integer +*/ +#[cfg(target_arch="x86")] +const min_value: int = -1 << 31; + +#[cfg(target_arch="x86_64")] +const min_value: int = -1 << 63; + +/* Function: add */ +pure fn add(x: int, y: int) -> int { ret x + y; } + +/* Function: sub */ +pure fn sub(x: int, y: int) -> int { ret x - y; } + +/* Function: mul */ +pure fn mul(x: int, y: int) -> int { ret x * y; } + +/* Function: div */ +pure fn div(x: int, y: int) -> int { ret x / y; } + +/* Function: rem */ +pure fn rem(x: int, y: int) -> int { ret x % y; } + +/* Predicate: lt */ +pure fn lt(x: int, y: int) -> bool { ret x < y; } + +/* Predicate: le */ +pure fn le(x: int, y: int) -> bool { ret x <= y; } + +/* Predicate: eq */ +pure fn eq(x: int, y: int) -> bool { ret x == y; } + +/* Predicate: ne */ +pure fn ne(x: int, y: int) -> bool { ret x != y; } + +/* Predicate: ge */ +pure fn ge(x: int, y: int) -> bool { ret x >= y; } + +/* Predicate: gt */ +pure fn gt(x: int, y: int) -> bool { ret x > y; } + +/* Predicate: positive */ +pure fn positive(x: int) -> bool { ret x > 0; } + +/* Predicate: negative */ +pure fn negative(x: int) -> bool { ret x < 0; } + +/* Predicate: nonpositive */ +pure fn nonpositive(x: int) -> bool { ret x <= 0; } + +/* Predicate: nonnegative */ +pure fn nonnegative(x: int) -> bool { ret x >= 0; } + + +// FIXME: Make sure this works with negative integers. +/* +Function: hash + +Produce a uint suitable for use in a hash table +*/ +fn hash(x: int) -> uint { ret x as uint; } + +/* +Function: range + +Iterate over the range [`lo`..`hi`) +*/ +fn range(lo: int, hi: int, it: block(int)) { + let i = lo; + while i < hi { it(i); i += 1; } +} + +/* +Function: parse_buf + +Parse a buffer of bytes + +Parameters: + +buf - A byte buffer +radix - The base of the number + +Failure: + +buf must not be empty +*/ +fn parse_buf(buf: [u8], radix: uint) -> int { + if vec::len::<u8>(buf) == 0u { + log_err "parse_buf(): buf is empty"; + fail; + } + let i = vec::len::<u8>(buf) - 1u; + let start = 0u; + let power = 1; + + if buf[0] == ('-' as u8) { + power = -1; + start = 1u; + } + let n = 0; + while true { + let digit = char::to_digit(buf[i] as char); + if (digit as uint) >= radix { + fail; + } + n += (digit as int) * power; + power *= radix as int; + if i <= start { ret n; } + i -= 1u; + } + fail; +} + +/* +Function: from_str + +Parse a string to an int + +Failure: + +s must not be empty +*/ +fn from_str(s: str) -> int { parse_buf(str::bytes(s), 10u) } + +/* +Function: to_str + +Convert to a string in a given base +*/ +fn to_str(n: int, radix: uint) -> str { + assert (0u < radix && radix <= 16u); + ret if n < 0 { + "-" + uint::to_str(-n as uint, radix) + } else { uint::to_str(n as uint, radix) }; +} + +/* +Function: str + +Convert to a string +*/ +fn str(i: int) -> str { ret to_str(i, 10u); } + +/* +Function: pow + +Returns `base` raised to the power of `exponent` +*/ +fn pow(base: int, exponent: uint) -> int { + if exponent == 0u { ret 1; } //Not mathemtically true if [base == 0] + if base == 0 { ret 0; } + let my_pow = exponent; + let acc = 1; + let multiplier = base; + while(my_pow > 0u) { + if my_pow % 2u == 1u { + acc *= multiplier; + } + my_pow /= 2u; + multiplier *= multiplier; + } + ret acc; +} +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/io.rs b/src/libstd/io.rs new file mode 100644 index 00000000000..910ec8bb9ea --- /dev/null +++ b/src/libstd/io.rs @@ -0,0 +1,582 @@ +import ctypes::fd_t; +import ctypes::c_int; + +#[abi = "cdecl"] +native mod rustrt { + fn rust_get_stdin() -> os::libc::FILE; + fn rust_get_stdout() -> os::libc::FILE; + fn rust_get_stderr() -> os::libc::FILE; +} + +// Reading + +// FIXME This is all buffered. We might need an unbuffered variant as well +tag seek_style { seek_set; seek_end; seek_cur; } + + +// The raw underlying reader class. All readers must implement this. +type buf_reader = + // FIXME: Seekable really should be orthogonal. We will need + // inheritance. + obj { + fn read(uint) -> [u8]; + fn read_byte() -> int; + fn unread_byte(int); + fn eof() -> bool; + fn seek(int, seek_style); + fn tell() -> uint; + // Needed on readers in case one needs to flush metadata + // changes (atime) + fn fsync(level: fsync::level) -> int; + }; + + +// Convenience methods for reading. +type reader = + // FIXME: This should inherit from buf_reader. + // FIXME: eventually u64 + + obj { + fn get_buf_reader() -> buf_reader; + fn read_byte() -> int; + fn unread_byte(int); + fn read_bytes(uint) -> [u8]; + fn read_char() -> char; + fn eof() -> bool; + fn read_line() -> str; + fn read_c_str() -> str; + fn read_le_uint(uint) -> uint; + fn read_le_int(uint) -> int; + fn read_be_uint(uint) -> uint; + fn read_whole_stream() -> [u8]; + fn seek(int, seek_style); + fn tell() -> uint; + }; + +fn convert_whence(whence: seek_style) -> i32 { + ret alt whence { + seek_set. { 0i32 } + seek_cur. { 1i32 } + seek_end. { 2i32 } + }; +} + +resource FILE_res(f: os::libc::FILE) { + os::libc::fclose(f); +} + +obj FILE_buf_reader(f: os::libc::FILE, res: option::t<@FILE_res>) { + fn read(len: uint) -> [u8] unsafe { + let buf = []; + vec::reserve::<u8>(buf, len); + let read = + os::libc::fread(vec::unsafe::to_ptr::<u8>(buf), 1u, len, f); + vec::unsafe::set_len::<u8>(buf, read); + ret buf; + } + fn read_byte() -> int { ret os::libc::fgetc(f) as int; } + fn unread_byte(byte: int) { os::libc::ungetc(byte as i32, f); } + fn eof() -> bool { ret os::libc::feof(f) != 0i32; } + fn seek(offset: int, whence: seek_style) { + assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0i32); + } + fn tell() -> uint { ret os::libc::ftell(f) as uint; } + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(os::libc::fileno(f), level) as int; + } +} + + +// FIXME: Convert this into pseudomethods on buf_reader. +obj new_reader(rdr: buf_reader) { + fn get_buf_reader() -> buf_reader { ret rdr; } + fn read_byte() -> int { ret rdr.read_byte(); } + fn unread_byte(byte: int) { ret rdr.unread_byte(byte); } + fn read_bytes(len: uint) -> [u8] { ret rdr.read(len); } + fn read_char() -> char { + let c0 = rdr.read_byte(); + if c0 == -1 { + ret -1 as char; // FIXME will this stay valid? + + } + let b0 = c0 as u8; + let w = str::utf8_char_width(b0); + assert (w > 0u); + if w == 1u { ret b0 as char; } + let val = 0u; + while w > 1u { + w -= 1u; + let next = rdr.read_byte(); + assert (next > -1); + assert (next & 192 == 128); + val <<= 6u; + val += next & 63 as uint; + } + // See str::char_at + + val += (b0 << (w + 1u as u8) as uint) << (w - 1u) * 6u - w - 1u; + ret val as char; + } + fn eof() -> bool { ret rdr.eof(); } + fn read_line() -> str { + let buf: [u8] = []; + // No break yet in rustc + + let go_on = true; + while go_on { + let ch = rdr.read_byte(); + if ch == -1 || ch == 10 { + go_on = false; + } else { buf += [ch as u8]; } + } + ret str::unsafe_from_bytes(buf); + } + fn read_c_str() -> str { + let buf: [u8] = []; + let go_on = true; + while go_on { + let ch = rdr.read_byte(); + if ch < 1 { go_on = false; } else { buf += [ch as u8]; } + } + ret str::unsafe_from_bytes(buf); + } + + // FIXME deal with eof? + fn read_le_uint(size: uint) -> uint { + let val = 0u; + let pos = 0u; + let i = size; + while i > 0u { + val += (rdr.read_byte() as uint) << pos; + pos += 8u; + i -= 1u; + } + ret val; + } + fn read_le_int(size: uint) -> int { + let val = 0u, pos = 0u, i = size; + while i > 0u { + val += (rdr.read_byte() as uint) << pos; + pos += 8u; + i -= 1u; + } + ret val as int; + } + + // FIXME deal with eof? + fn read_be_uint(sz: uint) -> uint { + let val = 0u, i = sz; + + while i > 0u { + i -= 1u; + val += (rdr.read_byte() as uint) << i * 8u; + } + ret val; + } + fn read_whole_stream() -> [u8] { + let buf: [u8] = []; + while !rdr.eof() { buf += rdr.read(2048u); } + ret buf; + } + fn seek(offset: int, whence: seek_style) { ret rdr.seek(offset, whence); } + fn tell() -> uint { ret rdr.tell(); } +} + +fn stdin() -> reader { + ret new_reader(FILE_buf_reader(rustrt::rust_get_stdin(), option::none)); +} + +fn file_reader(path: str) -> result::t<reader, str> { + let f = str::as_buf(path, {|pathbuf| + str::as_buf("r", {|modebuf| + os::libc::fopen(pathbuf, modebuf) + }) + }); + ret if f as uint == 0u { result::err("error opening " + path) } + else { + result::ok(new_reader(FILE_buf_reader(f, option::some(@FILE_res(f))))) + } +} + + +// Byte buffer readers + +// TODO: const u8, but this fails with rustboot. +type byte_buf = @{buf: [u8], mutable pos: uint}; + +obj byte_buf_reader(bbuf: byte_buf) { + fn read(len: uint) -> [u8] { + let rest = vec::len::<u8>(bbuf.buf) - bbuf.pos; + let to_read = len; + if rest < to_read { to_read = rest; } + let range = vec::slice::<u8>(bbuf.buf, bbuf.pos, bbuf.pos + to_read); + bbuf.pos += to_read; + ret range; + } + fn read_byte() -> int { + if bbuf.pos == vec::len::<u8>(bbuf.buf) { ret -1; } + let b = bbuf.buf[bbuf.pos]; + bbuf.pos += 1u; + ret b as int; + } + fn unread_byte(_byte: int) { log_err "TODO: unread_byte"; fail; } + fn eof() -> bool { ret bbuf.pos == vec::len::<u8>(bbuf.buf); } + fn seek(offset: int, whence: seek_style) { + let pos = bbuf.pos; + let len = vec::len::<u8>(bbuf.buf); + bbuf.pos = seek_in_buf(offset, pos, len, whence); + } + fn tell() -> uint { ret bbuf.pos; } + fn fsync(_level: fsync::level) -> int { ret 0; } +} + +fn new_byte_buf_reader(buf: [u8]) -> buf_reader { + ret byte_buf_reader(@{buf: buf, mutable pos: 0u}); +} + +fn string_reader(s: str) -> reader { + ret new_reader(new_byte_buf_reader(str::bytes(s))); +} + + +// Writing +tag fileflag { append; create; truncate; none; } + +type buf_writer = + // FIXME: Seekable really should be orthogonal. We will need + // inheritance. + // FIXME: eventually u64 + + obj { + fn write([u8]); + fn seek(int, seek_style); + fn tell() -> uint; + fn flush() -> int; + fn fsync(level: fsync::level) -> int; + }; + +obj FILE_writer(f: os::libc::FILE, res: option::t<@FILE_res>) { + fn write(v: [u8]) unsafe { + let len = vec::len::<u8>(v); + let vbuf = vec::unsafe::to_ptr::<u8>(v); + let nout = os::libc::fwrite(vbuf, len, 1u, f); + if nout < 1u { log_err "error dumping buffer"; } + } + fn seek(offset: int, whence: seek_style) { + assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0i32); + } + fn tell() -> uint { ret os::libc::ftell(f) as uint; } + fn flush() -> int { ret os::libc::fflush(f) as int; } + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(os::libc::fileno(f), level) as int; + } +} + +resource fd_res(fd: fd_t) { os::libc::close(fd); } + +obj fd_buf_writer(fd: fd_t, res: option::t<@fd_res>) { + fn write(v: [u8]) unsafe { + let len = vec::len::<u8>(v); + let count = 0u; + let vbuf; + while count < len { + vbuf = ptr::offset(vec::unsafe::to_ptr::<u8>(v), count); + let nout = os::libc::write(fd, vbuf, len); + if nout < 0 { + log_err "error dumping buffer"; + log_err sys::last_os_error(); + fail; + } + count += nout as uint; + } + } + fn seek(_offset: int, _whence: seek_style) { + log_err "need 64-bit native calls for seek, sorry"; + fail; + } + fn tell() -> uint { + log_err "need 64-bit native calls for tell, sorry"; + fail; + } + + fn flush() -> int { ret 0; } + + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(fd, level) as int; + } +} + +fn file_buf_writer(path: str, + flags: [fileflag]) -> result::t<buf_writer, str> { + let fflags: i32 = + os::libc_constants::O_WRONLY | os::libc_constants::O_BINARY; + for f: fileflag in flags { + alt f { + append. { fflags |= os::libc_constants::O_APPEND; } + create. { fflags |= os::libc_constants::O_CREAT; } + truncate. { fflags |= os::libc_constants::O_TRUNC; } + none. { } + } + } + let fd = + str::as_buf(path, + {|pathbuf| + os::libc::open(pathbuf, fflags, + os::libc_constants::S_IRUSR | + os::libc_constants::S_IWUSR) + }); + ret if fd < 0i32 { + log_err sys::last_os_error(); + result::err("error opening " + path) + } else { + result::ok(fd_buf_writer(fd, option::some(@fd_res(fd)))) + } +} + +type writer = + // write_str will continue to do utf-8 output only. an alternative + // function will be provided for general encoded string output + obj { + fn get_buf_writer() -> buf_writer; + fn write_str(str); + fn write_line(str); + fn write_char(char); + fn write_int(int); + fn write_uint(uint); + fn write_bytes([u8]); + fn write_le_uint(uint, uint); + fn write_le_int(int, uint); + fn write_be_uint(uint, uint); + }; + +fn uint_to_le_bytes(n: uint, size: uint) -> [u8] { + let bytes: [u8] = [], i = size, n = n; + while i > 0u { bytes += [n & 255u as u8]; n >>= 8u; i -= 1u; } + ret bytes; +} + +fn uint_to_be_bytes(n: uint, size: uint) -> [u8] { + let bytes: [u8] = []; + let i = size - 1u as int; + while i >= 0 { bytes += [n >> (i * 8 as uint) & 255u as u8]; i -= 1; } + ret bytes; +} + +obj new_writer(out: buf_writer) { + fn get_buf_writer() -> buf_writer { ret out; } + fn write_str(s: str) { out.write(str::bytes(s)); } + fn write_line(s: str) { + out.write(str::bytes(s)); + out.write(str::bytes("\n")); + } + fn write_char(ch: char) { + // FIXME needlessly consy + + out.write(str::bytes(str::from_char(ch))); + } + fn write_int(n: int) { out.write(str::bytes(int::to_str(n, 10u))); } + fn write_uint(n: uint) { out.write(str::bytes(uint::to_str(n, 10u))); } + fn write_bytes(bytes: [u8]) { out.write(bytes); } + fn write_le_uint(n: uint, size: uint) { + out.write(uint_to_le_bytes(n, size)); + } + fn write_le_int(n: int, size: uint) { + out.write(uint_to_le_bytes(n as uint, size)); + } + fn write_be_uint(n: uint, size: uint) { + out.write(uint_to_be_bytes(n, size)); + } +} + +fn file_writer(path: str, flags: [fileflag]) -> result::t<writer, str> { + result::chain(file_buf_writer(path, flags), { |w| + result::ok(new_writer(w)) + }) +} + + +// FIXME: fileflags +fn buffered_file_buf_writer(path: str) -> result::t<buf_writer, str> { + let f = + str::as_buf(path, + {|pathbuf| + str::as_buf("w", + {|modebuf| + os::libc::fopen(pathbuf, modebuf) + }) + }); + ret if f as uint == 0u { result::err("error opening " + path) } + else { result::ok(FILE_writer(f, option::some(@FILE_res(f)))) } +} + + +// FIXME it would be great if this could be a const +// Problem seems to be that new_writer is not pure +fn stdout() -> writer { ret new_writer(fd_buf_writer(1i32, option::none)); } +fn stderr() -> writer { ret new_writer(fd_buf_writer(2i32, option::none)); } + +fn print(s: str) { stdout().write_str(s); } +fn println(s: str) { stdout().write_str(s + "\n"); } + +type str_writer = + obj { + fn get_writer() -> writer; + fn get_str() -> str; + }; + +type mutable_byte_buf = @{mutable buf: [mutable u8], mutable pos: uint}; + +obj byte_buf_writer(buf: mutable_byte_buf) { + fn write(v: [u8]) { + // Fast path. + + if buf.pos == vec::len(buf.buf) { + for b: u8 in v { buf.buf += [mutable b]; } + buf.pos += vec::len::<u8>(v); + ret; + } + // FIXME: Optimize: These should be unique pointers. + + let vlen = vec::len::<u8>(v); + let vpos = 0u; + while vpos < vlen { + let b = v[vpos]; + if buf.pos == vec::len(buf.buf) { + buf.buf += [mutable b]; + } else { buf.buf[buf.pos] = b; } + buf.pos += 1u; + vpos += 1u; + } + } + fn seek(offset: int, whence: seek_style) { + let pos = buf.pos; + let len = vec::len(buf.buf); + buf.pos = seek_in_buf(offset, pos, len, whence); + } + fn tell() -> uint { ret buf.pos; } + fn flush() -> int { ret 0; } + fn fsync(_level: fsync::level) -> int { ret 0; } +} + +fn string_writer() -> str_writer { + // FIXME: yikes, this is bad. Needs fixing of mutable syntax. + + let b: [mutable u8] = [mutable 0u8]; + vec::pop(b); + let buf: mutable_byte_buf = @{mutable buf: b, mutable pos: 0u}; + obj str_writer_wrap(wr: writer, buf: mutable_byte_buf) { + fn get_writer() -> writer { ret wr; } + fn get_str() -> str { ret str::unsafe_from_bytes(buf.buf); } + } + ret str_writer_wrap(new_writer(byte_buf_writer(buf)), buf); +} + + +// Utility functions +fn seek_in_buf(offset: int, pos: uint, len: uint, whence: seek_style) -> + uint { + let bpos = pos as int; + let blen = len as int; + alt whence { + seek_set. { bpos = offset; } + seek_cur. { bpos += offset; } + seek_end. { bpos = blen + offset; } + } + if bpos < 0 { bpos = 0; } else if bpos > blen { bpos = blen; } + ret bpos as uint; +} + +fn read_whole_file_str(file: str) -> result::t<str, str> { + result::chain(read_whole_file(file), { |bytes| + result::ok(str::unsafe_from_bytes(bytes)) + }) +} + +fn read_whole_file(file: str) -> result::t<[u8], str> { + + // FIXME: There's a lot of copying here + result::chain(file_reader(file), { |rdr| + result::ok(rdr.read_whole_stream()) + }) +} + +// fsync related + +mod fsync { + + tag level { + // whatever fsync does on that platform + fsync; + + // fdatasync on linux, similiar or more on other platforms + fdatasync; + + // full fsync + // + // You must additionally sync the parent directory as well! + fullfsync; + } + + + // Resource of artifacts that need to fsync on destruction + resource res<t>(arg: arg<t>) { + alt arg.opt_level { + option::none::<level>. { } + option::some::<level>(level) { + // fail hard if not succesful + assert(arg.fsync_fn(arg.val, level) != -1); + } + } + } + + type arg<t> = { + val: t, + opt_level: option::t<level>, + fsync_fn: fn(t, level) -> int + }; + + // fsync file after executing blk + // FIXME find better way to create resources within lifetime of outer res + fn FILE_res_sync(&&file: FILE_res, opt_level: option::t<level>, + blk: block(&&res<os::libc::FILE>)) { + blk(res({ + val: *file, opt_level: opt_level, + fsync_fn: fn(&&file: os::libc::FILE, l: level) -> int { + ret os::fsync_fd(os::libc::fileno(file), l) as int; + } + })); + } + + // fsync fd after executing blk + fn fd_res_sync(&&fd: fd_res, opt_level: option::t<level>, + blk: block(&&res<fd_t>)) { + blk(res({ + val: *fd, opt_level: opt_level, + fsync_fn: fn(&&fd: fd_t, l: level) -> int { + ret os::fsync_fd(fd, l) as int; + } + })); + } + + // Type of objects that may want to fsync + type t = obj { fn fsync(l: level) -> int; }; + + // Call o.fsync after executing blk + fn obj_sync(&&o: t, opt_level: option::t<level>, blk: block(&&res<t>)) { + blk(res({ + val: o, opt_level: opt_level, + fsync_fn: fn(&&o: t, l: level) -> int { ret o.fsync(l); } + })); + } +} + + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/libstd/json.rs b/src/libstd/json.rs new file mode 100644 index 00000000000..3f74c55507b --- /dev/null +++ b/src/libstd/json.rs @@ -0,0 +1,258 @@ +// Rust JSON serialization library +// Copyright (c) 2011 Google Inc. + +import float; +import map; +import option; +import option::{some, none}; +import str; +import vec; + +export json; +export to_str; +export from_str; + +export num; +export string; +export boolean; +export list; +export dict; + +/* +Tag: json + +Represents a json value. +*/ +tag json { + /* Variant: num */ + num(float); + /* Variant: string */ + string(str); + /* Variant: boolean */ + boolean(bool); + /* Variant: list */ + list(@[json]); + /* Variant: dict */ + dict(map::hashmap<str,json>); +} + +/* +Function: to_str + +Serializes a json value into a string. +*/ +fn to_str(j: json) -> str { + alt j { + num(f) { float::to_str(f, 6u) } + string(s) { #fmt["\"%s\"", s] } // XXX: escape + boolean(true) { "true" } + boolean(false) { "false" } + list(@js) { + str::concat(["[", + str::connect( + vec::map::<json,str>({ |e| to_str(e) }, js), + ", "), + "]"]) + } + dict(m) { + let parts = []; + m.items({ |k, v| + vec::grow(parts, 1u, + str::concat(["\"", k, "\": ", to_str(v)]) + ) + }); + str::concat(["{ ", str::connect(parts, ", "), " }"]) + } + } +} + +fn rest(s: str) -> str { + assert(str::char_len(s) >= 1u); + str::char_slice(s, 1u, str::char_len(s)) +} + +fn from_str_str(s: str) -> (option::t<json>, str) { + let pos = 0u; + let len = str::byte_len(s); + let escape = false; + let res = ""; + + alt str::char_at(s, 0u) { + '"' { pos = 1u; } + _ { ret (none, s); } + } + + while (pos < len) { + let chr = str::char_range_at(s, pos); + let c = chr.ch; + pos = chr.next; + if (escape) { + res = res + str::from_char(c); + escape = false; + cont; + } + if (c == '\\') { + escape = true; + cont; + } else if (c == '"') { + ret (some(string(res)), + str::char_slice(s, pos, str::char_len(s))); + } + res = res + str::from_char(c); + } + + ret (none, s); +} + +fn from_str_list(s: str) -> (option::t<json>, str) { + if str::char_at(s, 0u) != '[' { ret (none, s); } + let s0 = str::trim_left(rest(s)); + let vals = []; + if str::is_empty(s0) { ret (none, s0); } + if str::char_at(s0, 0u) == ']' { ret (some(list(@[])), rest(s0)); } + while str::is_not_empty(s0) { + s0 = str::trim_left(s0); + let (next, s1) = from_str_helper(s0); + s0 = s1; + alt next { + some(j) { vec::grow(vals, 1u, j); } + none { ret (none, s0); } + } + s0 = str::trim_left(s0); + if str::is_empty(s0) { ret (none, s0); } + alt str::char_at(s0, 0u) { + ',' { } + ']' { ret (some(list(@vals)), rest(s0)); } + _ { ret (none, s0); } + } + s0 = rest(s0); + } + ret (none, s0); +} + +fn from_str_dict(s: str) -> (option::t<json>, str) { + if str::char_at(s, 0u) != '{' { ret (none, s); } + let s0 = str::trim_left(rest(s)); + let vals = map::new_str_hash::<json>(); + if str::is_empty(s0) { ret (none, s0); } + if str::char_at(s0, 0u) == '}' { ret (some(dict(vals)), rest(s0)); } + while str::is_not_empty(s0) { + s0 = str::trim_left(s0); + let (next, s1) = from_str_helper(s0); // key + let key = ""; + s0 = s1; + alt next { + some(string(k)) { key = k; } + _ { ret (none, s0); } + } + s0 = str::trim_left(s0); + if str::is_empty(s0) { ret (none, s0); } + if str::char_at(s0, 0u) != ':' { ret (none, s0); } + s0 = str::trim_left(rest(s0)); + let (next, s1) = from_str_helper(s0); // value + s0 = s1; + alt next { + some(j) { vals.insert(key, j); } + _ { ret (none, s0); } + } + s0 = str::trim_left(s0); + if str::is_empty(s0) { ret (none, s0); } + alt str::char_at(s0, 0u) { + ',' { } + '}' { ret (some(dict(vals)), rest(s0)); } + _ { ret (none, s0); } + } + s0 = str::trim_left(rest(s0)); + } + (none, s) +} + +fn from_str_float(s: str) -> (option::t<json>, str) { + let pos = 0u; + let len = str::byte_len(s); + let res = 0f; + let neg = 1.f; + + alt str::char_at(s, 0u) { + '-' { + neg = -1.f; + pos = 1u; + } + '+' { + pos = 1u; + } + '0' to '9' | '.' { } + _ { ret (none, s); } + } + + while (pos < len) { + let opos = pos; + let chr = str::char_range_at(s, pos); + let c = chr.ch; + pos = chr.next; + alt c { + '0' to '9' { + res = res * 10f; + res += ((c as int) - ('0' as int)) as float; + } + '.' { break; } + _ { ret (some(num(neg * res)), + str::char_slice(s, opos, str::char_len(s))); } + } + } + + if pos == len { + ret (some(num(neg * res)), str::char_slice(s, pos, str::char_len(s))); + } + + let dec = 1.f; + while (pos < len) { + let opos = pos; + let chr = str::char_range_at(s, pos); + let c = chr.ch; + pos = chr.next; + alt c { + '0' to '9' { + dec /= 10.f; + res += (((c as int) - ('0' as int)) as float) * dec; + } + _ { ret (some(num(neg * res)), + str::char_slice(s, opos, str::char_len(s))); } + } + } + ret (some(num(neg * res)), str::char_slice(s, pos, str::char_len(s))); +} + +fn from_str_bool(s: str) -> (option::t<json>, str) { + if (str::starts_with(s, "true")) { + (some(boolean(true)), str::slice(s, 4u, str::byte_len(s))) + } else if (str::starts_with(s, "false")) { + (some(boolean(false)), str::slice(s, 5u, str::byte_len(s))) + } else { + (none, s) + } +} + +fn from_str_helper(s: str) -> (option::t<json>, str) { + let s = str::trim_left(s); + if str::is_empty(s) { ret (none, s); } + let start = str::char_at(s, 0u); + alt start { + '"' { from_str_str(s) } + '[' { from_str_list(s) } + '{' { from_str_dict(s) } + '0' to '9' | '-' | '+' | '.' { from_str_float(s) } + 't' | 'f' { from_str_bool(s) } + _ { ret (none, s); } + } +} + +/* +Function: from_str + +Deserializes a json value from a string. +*/ +fn from_str(s: str) -> option::t<json> { + let (j, _) = from_str_helper(s); + j +} diff --git a/src/libstd/linux_os.rs b/src/libstd/linux_os.rs new file mode 100644 index 00000000000..7e30088ee3c --- /dev/null +++ b/src/libstd/linux_os.rs @@ -0,0 +1,145 @@ +/* +Module: os + +TODO: Restructure and document +*/ + +import ctypes::*; + +export libc; +export libc_constants; +export pipe; +export fd_FILE; +export close; +export fclose; +export waitpid; +export getcwd; +export exec_suffix; +export target_os; +export dylib_filename; +export get_exe_path; +export fsync_fd; + +// FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult +// by https://github.com/graydon/rust/issues#issue/268 + +#[link_name = ""] +#[abi = "cdecl"] +native mod libc { + fn read(fd: fd_t, buf: *u8, count: size_t) -> ssize_t; + fn write(fd: fd_t, buf: *u8, count: size_t) -> ssize_t; + fn fread(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t; + fn fwrite(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t; + fn open(s: str::sbuf, flags: c_int, mode: unsigned) -> fd_t; + fn close(fd: fd_t) -> c_int; + type FILE; + fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; + fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE; + fn fclose(f: FILE); + fn fflush(f: FILE) -> c_int; + fn fsync(fd: fd_t) -> c_int; + fn fdatasync(fd: fd_t) -> c_int; + fn fileno(f: FILE) -> fd_t; + fn fgetc(f: FILE) -> c_int; + fn ungetc(c: c_int, f: FILE); + fn feof(f: FILE) -> c_int; + fn fseek(f: FILE, offset: long, whence: c_int) -> c_int; + fn ftell(f: FILE) -> long; + type dir; + fn opendir(d: str::sbuf) -> dir; + fn closedir(d: dir) -> c_int; + type dirent; + fn readdir(d: dir) -> dirent; + fn getenv(n: str::sbuf) -> str::sbuf; + fn setenv(n: str::sbuf, v: str::sbuf, overwrite: c_int) -> c_int; + fn unsetenv(n: str::sbuf) -> c_int; + fn pipe(buf: *mutable fd_t) -> c_int; + fn waitpid(pid: pid_t, &status: c_int, options: c_int) -> pid_t; + fn readlink(path: str::sbuf, buf: str::sbuf, bufsize: size_t) -> ssize_t; + fn mkdir(path: str::sbuf, mode: c_int) -> c_int; + fn rmdir(path: str::sbuf) -> c_int; + fn chdir(path: str::sbuf) -> c_int; +} + +mod libc_constants { + const O_RDONLY: c_int = 0i32; + const O_WRONLY: c_int = 1i32; + const O_RDWR: c_int = 2i32; + const O_APPEND: c_int = 1024i32; + const O_CREAT: c_int = 64i32; + const O_EXCL: c_int = 128i32; + const O_TRUNC: c_int = 512i32; + const O_TEXT: c_int = 0i32; // nonexistent in linux libc + const O_BINARY: c_int = 0i32; // nonexistent in linux libc + + const S_IRUSR: unsigned = 256u32; + const S_IWUSR: unsigned = 128u32; +} + +fn pipe() -> {in: fd_t, out: fd_t} { + let fds = {mutable in: 0i32, mutable out: 0i32}; + assert (os::libc::pipe(ptr::mut_addr_of(fds.in)) == 0i32); + ret {in: fds.in, out: fds.out}; +} + +fn fd_FILE(fd: fd_t) -> libc::FILE { + ret str::as_buf("r", {|modebuf| libc::fdopen(fd, modebuf) }); +} + +fn close(fd: fd_t) -> c_int { + libc::close(fd) +} + +fn fclose(file: libc::FILE) { + libc::fclose(file) +} + +fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { + alt level { + io::fsync::fsync. | io::fsync::fullfsync. { ret libc::fsync(fd); } + io::fsync::fdatasync. { ret libc::fdatasync(fd); } + } +} + +fn waitpid(pid: pid_t) -> i32 { + let status = 0i32; + assert (os::libc::waitpid(pid, status, 0i32) != -1i32); + ret status; +} + +#[abi = "cdecl"] +native mod rustrt { + fn rust_getcwd() -> str; +} + +fn getcwd() -> str { ret rustrt::rust_getcwd(); } + +fn exec_suffix() -> str { ret ""; } + +fn target_os() -> str { ret "linux"; } + +fn dylib_filename(base: str) -> str { ret "lib" + base + ".so"; } + +/// Returns the directory containing the running program +/// followed by a path separator +fn get_exe_path() -> option::t<fs::path> { + let bufsize = 1023u; + let path = str::unsafe_from_bytes(vec::init_elt(0u8, bufsize)); + ret str::as_buf("/proc/self/exe", { |proc_self_buf| + str::as_buf(path, { |path_buf| + if libc::readlink(proc_self_buf, path_buf, bufsize) != -1 { + option::some(fs::dirname(path) + fs::path_sep()) + } else { + option::none + } + }) + }); +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/list.rs b/src/libstd/list.rs new file mode 100644 index 00000000000..ab712091819 --- /dev/null +++ b/src/libstd/list.rs @@ -0,0 +1,161 @@ +/* +Module: list + +A standard linked list +*/ + +import option::{some, none}; + +/* Section: Types */ + +/* +Tag: list +*/ +tag list<T> { + /* Variant: cons */ + cons(T, @list<T>); + /* Variant: nil */ + nil; +} + +/*Section: Operations */ + +/* +Function: from_vec + +Create a list from a vector +*/ +fn from_vec<copy T>(v: [const T]) -> list<T> { + *vec::foldr({ |h, t| @cons(h, t) }, @nil::<T>, v) +} + +/* +Function: foldl + +Left fold + +Applies `f` to `u` and the first element in the list, then applies +`f` to the result of the previous call and the second element, +and so on, returning the accumulated result. + +Parameters: + +ls - The list to fold +z - The initial value +f - The function to apply +*/ +fn foldl<copy T, copy U>(ls: list<U>, z: T, f: block(T, U) -> T) -> T { + let accum: T = z; + let ls = ls; + while true { + alt ls { + cons(hd, tl) { accum = f(accum, hd); ls = *tl; } + nil. { break; } + } + } + ret accum; +} + +/* +Function: find + +Search for an element that matches a given predicate + +Apply function `f` to each element of `v`, starting from the first. +When function `f` returns true then an option containing the element +is returned. If `f` matches no elements then none is returned. +*/ +fn find<copy T, copy U>(ls: list<T>, f: block(T) -> option::t<U>) + -> option::t<U> { + let ls = ls; + while true { + alt ls { + cons(hd, tl) { + alt f(hd) { none. { ls = *tl; } some(rs) { ret some(rs); } } + } + nil. { break; } + } + } + ret none; +} + +/* +Function: has + +Returns true if a list contains an element with the given value +*/ +fn has<copy T>(ls: list<T>, elt: T) -> bool { + let ls = ls; + while true { + alt ls { + cons(hd, tl) { if elt == hd { ret true; } else { ls = *tl; } } + nil. { break; } + } + } + ret false; +} + +/* +Function: len + +Returns the length of a list +*/ +fn len<copy T>(ls: list<T>) -> uint { + fn count<T>(&&u: uint, _t: T) -> uint { ret u + 1u; } + ret foldl(ls, 0u, bind count(_, _)); +} + +/* +Function: tail + +Returns all but the first element of a list +*/ +fn tail<copy T>(ls: list<T>) -> list<T> { + alt ls { cons(_, tl) { ret *tl; } nil. { fail "list empty" } } +} + +/* +Function: head + +Returns the first element of a list +*/ +fn head<copy T>(ls: list<T>) -> T { + alt ls { cons(hd, _) { ret hd; } nil. { fail "list empty" } } +} + +/* +Function: append + +Appends one list to another +*/ +fn append<copy T>(l: list<T>, m: list<T>) -> list<T> { + alt l { + nil. { ret m; } + cons(x, xs) { let rest = append(*xs, m); ret cons(x, @rest); } + } +} + +/* +Function: iter + +Iterate over a list +*/ +fn iter<copy T>(l: list<T>, f: block(T)) { + let cur = l; + while cur != nil { + alt cur { + cons(hd, tl) { + f(hd); + cur = *tl; + } + } + } +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/macos_os.rs b/src/libstd/macos_os.rs new file mode 100644 index 00000000000..f23e82eaa63 --- /dev/null +++ b/src/libstd/macos_os.rs @@ -0,0 +1,153 @@ +import ctypes::*; + +export libc; +export libc_constants; +export pipe; +export fd_FILE; +export close; +export fclose; +export waitpid; +export getcwd; +export exec_suffix; +export target_os; +export dylib_filename; +export get_exe_path; +export fsync_fd; + +// FIXME Refactor into unix_os module or some such. Doesn't +// seem to work right now. + +#[link_name = ""] +#[abi = "cdecl"] +native mod libc { + fn read(fd: fd_t, buf: *u8, count: size_t) -> ssize_t; + fn write(fd: fd_t, buf: *u8, count: size_t) -> ssize_t; + fn fread(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t; + fn fwrite(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t; + fn open(s: str::sbuf, flags: c_int, mode: unsigned) -> fd_t; + fn close(fd: fd_t) -> c_int; + type FILE; + fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; + fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE; + fn fflush(f: FILE) -> c_int; + fn fsync(fd: fd_t) -> c_int; + fn fileno(f: FILE) -> fd_t; + fn fclose(f: FILE); + fn fgetc(f: FILE) -> c_int; + fn ungetc(c: c_int, f: FILE); + fn feof(f: FILE) -> c_int; + fn fseek(f: FILE, offset: long, whence: c_int) -> c_int; + fn ftell(f: FILE) -> long; + type dir; + fn opendir(d: str::sbuf) -> dir; + fn closedir(d: dir) -> c_int; + type dirent; + fn readdir(d: dir) -> dirent; + fn getenv(n: str::sbuf) -> str::sbuf; + fn setenv(n: str::sbuf, v: str::sbuf, overwrite: c_int) -> c_int; + fn unsetenv(n: str::sbuf) -> c_int; + fn pipe(buf: *mutable c_int) -> c_int; + fn waitpid(pid: pid_t, &status: c_int, options: c_int) -> c_int; + fn mkdir(s: str::sbuf, mode: c_int) -> c_int; + fn rmdir(s: str::sbuf) -> c_int; + fn chdir(s: str::sbuf) -> c_int; + + // FIXME: Needs varags + fn fcntl(fd: fd_t, cmd: c_int) -> c_int; +} + +mod libc_constants { + const O_RDONLY: c_int = 0i32; + const O_WRONLY: c_int = 1i32; + const O_RDWR: c_int = 2i32; + const O_APPEND: c_int = 8i32; + const O_CREAT: c_int = 512i32; + const O_EXCL: c_int = 2048i32; + const O_TRUNC: c_int = 1024i32; + const O_TEXT: c_int = 0i32; // nonexistent in darwin libc + const O_BINARY: c_int = 0i32; // nonexistent in darwin libc + + const S_IRUSR: unsigned = 256u32; + const S_IWUSR: unsigned = 128u32; + + const F_FULLFSYNC: c_int = 51i32; +} + +fn pipe() -> {in: fd_t, out: fd_t} { + let fds = {mutable in: 0i32, mutable out: 0i32}; + assert (os::libc::pipe(ptr::mut_addr_of(fds.in)) == 0i32); + ret {in: fds.in, out: fds.out}; +} + +fn fd_FILE(fd: fd_t) -> libc::FILE { + ret str::as_buf("r", {|modebuf| libc::fdopen(fd, modebuf) }); +} + +fn close(fd: fd_t) -> c_int { + libc::close(fd) +} + +fn fclose(file: libc::FILE) { + libc::fclose(file) +} + +fn waitpid(pid: pid_t) -> i32 { + let status = 0i32; + assert (os::libc::waitpid(pid, status, 0i32) != -1i32); + ret status; +} + +fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { + alt level { + io::fsync::fsync. { ret libc::fsync(fd); } + _ { + // According to man fnctl, the ok retval is only specified to be !=-1 + if (libc::fcntl(libc_constants::F_FULLFSYNC, fd) == -1 as c_int) + { ret -1 as c_int; } + else + { ret 0 as c_int; } + } + } +} + +#[abi = "cdecl"] +native mod rustrt { + fn rust_getcwd() -> str; +} + +fn getcwd() -> str { ret rustrt::rust_getcwd(); } + +#[link_name = ""] +#[abi = "cdecl"] +native mod mac_libc { + fn _NSGetExecutablePath(buf: str::sbuf, + bufsize: *mutable uint32_t) -> c_int; +} + +fn exec_suffix() -> str { ret ""; } + +fn target_os() -> str { ret "macos"; } + +fn dylib_filename(base: str) -> str { ret "lib" + base + ".dylib"; } + +fn get_exe_path() -> option::t<fs::path> { + // FIXME: This doesn't handle the case where the buffer is too small + let bufsize = 1023u32; + let path = str::unsafe_from_bytes(vec::init_elt(0u8, bufsize as uint)); + ret str::as_buf(path, { |path_buf| + if mac_libc::_NSGetExecutablePath(path_buf, + ptr::mut_addr_of(bufsize)) == 0i32 { + option::some(fs::dirname(path) + fs::path_sep()) + } else { + option::none + } + }); +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/map.rs b/src/libstd/map.rs new file mode 100644 index 00000000000..c802b5b889f --- /dev/null +++ b/src/libstd/map.rs @@ -0,0 +1,336 @@ +/* +Module: map + +A hashmap +*/ + +/* Section: Types */ + +/* +Type: hashfn + +A function that returns a hash of a value +*/ +type hashfn<K> = fn(K) -> uint; + +/* +Type: eqfn + +Equality +*/ +type eqfn<K> = fn(K, K) -> bool; + +/* +Type: hashset + +A convenience type to treat a hashmap as a set +*/ +type hashset<K> = hashmap<K, ()>; + +/* +Obj: hashmap +*/ +type hashmap<K, V> = obj { + /* + Method: size + + Return the number of elements in the map + */ + fn size() -> uint; + /* + Method: insert + + Add a value to the map. If the map already contains a value for + the specified key then the original value is replaced. + + Returns: + + True if the key did not already exist in the map + */ + fn insert(K, V) -> bool; + /* + Method: contains_key + + Returns true if the map contains a value for the specified key + */ + fn contains_key(K) -> bool; + /* + Method: get + + Get the value for the specified key + + Failure: + + If the key does not exist in the map + */ + fn get(K) -> V; + /* + Method: find + + Get the value for the specified key. If the key does not exist + in the map then returns none. + */ + fn find(K) -> option::t<V>; + /* + Method: remove + + Remove and return a value from the map. If the key does not exist + in the map then returns none. + */ + fn remove(K) -> option::t<V>; + /* + Method: rehash + + Force map growth and rehashing + */ + fn rehash(); + /* + Method: items + + Iterate over all the key/value pairs in the map + */ + fn items(block(K, V)); + /* + Method: keys + + Iterate over all the keys in the map + */ + fn keys(block(K)); + /* + Iterate over all the values in the map + */ + fn values(block(V)); +}; + +/* Section: Operations */ + +/* +Function: mk_hashmap + +Construct a hashmap + +Parameters: + +hasher - The hash function for key type K +eqer - The equality function for key type K +*/ +fn mk_hashmap<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>) + -> hashmap<K, V> { + let initial_capacity: uint = 32u; // 2^5 + + let load_factor: util::rational = {num: 3, den: 4}; + tag bucket<copy K, copy V> { nil; deleted; some(K, V); } + fn make_buckets<copy K, copy V>(nbkts: uint) -> [mutable bucket<K, V>] { + ret vec::init_elt_mut::<bucket<K, V>>(nil::<K, V>, nbkts); + } + // Derive two hash functions from the one given by taking the upper + // half and lower half of the uint bits. Our bucket probing + // sequence is then defined by + // + // hash(key, i) := hashl(key) * i + hashr(key) for i = 0, 1, 2, ... + // + // Tearing the hash function apart this way is kosher in practice + // as, assuming 32-bit uints, the table would have to be at 2^32 + // buckets before the resulting pair of hash functions no longer + // probes all buckets for a fixed key. Note that hashl is made to + // output odd numbers (hence coprime to the number of nbkts, which + // is always a power of 2), so that all buckets are probed for a + // fixed key. + + fn hashl(n: uint, _nbkts: uint) -> uint { ret (n >>> 16u) * 2u + 1u; } + fn hashr(n: uint, _nbkts: uint) -> uint { ret 0x0000_ffff_u & n; } + fn hash(h: uint, nbkts: uint, i: uint) -> uint { + ret (hashl(h, nbkts) * i + hashr(h, nbkts)) % nbkts; + } + /** + * We attempt to never call this with a full table. If we do, it + * will fail. + */ + + fn insert_common<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>, + bkts: [mutable bucket<K, V>], + nbkts: uint, key: K, val: V) -> bool { + let i: uint = 0u; + let h: uint = hasher(key); + while i < nbkts { + let j: uint = hash(h, nbkts, i); + alt bkts[j] { + some(k, _) { + // Copy key to please alias analysis. + + let k_ = k; + if eqer(key, k_) { bkts[j] = some(k_, val); ret false; } + i += 1u; + } + _ { bkts[j] = some(key, val); ret true; } + } + } + fail; // full table + } + fn find_common<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>, + bkts: [mutable bucket<K, V>], + nbkts: uint, key: K) -> option::t<V> { + let i: uint = 0u; + let h: uint = hasher(key); + while i < nbkts { + let j: uint = hash(h, nbkts, i); + alt bkts[j] { + some(k, v) { + // Copy to please alias analysis. + let k_ = k; + let v_ = v; + if eqer(key, k_) { ret option::some(v_); } + } + nil. { ret option::none; } + deleted. { } + } + i += 1u; + } + ret option::none; + } + fn rehash<copy K, copy V>(hasher: hashfn<K>, eqer: eqfn<K>, + oldbkts: [mutable bucket<K, V>], + _noldbkts: uint, + newbkts: [mutable bucket<K, V>], + nnewbkts: uint) { + for b: bucket<K, V> in oldbkts { + alt b { + some(k_, v_) { + let k = k_; + let v = v_; + insert_common(hasher, eqer, newbkts, nnewbkts, k, v); + } + _ { } + } + } + } + obj hashmap<copy K, copy V>(hasher: hashfn<K>, + eqer: eqfn<K>, + mutable bkts: [mutable bucket<K, V>], + mutable nbkts: uint, + mutable nelts: uint, + lf: util::rational) { + fn size() -> uint { ret nelts; } + fn insert(key: K, val: V) -> bool { + let load: util::rational = + {num: nelts + 1u as int, den: nbkts as int}; + if !util::rational_leq(load, lf) { + let nnewbkts: uint = uint::next_power_of_two(nbkts + 1u); + let newbkts = make_buckets(nnewbkts); + rehash(hasher, eqer, bkts, nbkts, newbkts, nnewbkts); + bkts = newbkts; + nbkts = nnewbkts; + } + if insert_common(hasher, eqer, bkts, nbkts, key, val) { + nelts += 1u; + ret true; + } + ret false; + } + fn contains_key(key: K) -> bool { + ret alt find_common(hasher, eqer, bkts, nbkts, key) { + option::some(_) { true } + _ { false } + }; + } + fn get(key: K) -> V { + ret alt find_common(hasher, eqer, bkts, nbkts, key) { + option::some(val) { val } + _ { fail } + }; + } + fn find(key: K) -> option::t<V> { + be find_common(hasher, eqer, bkts, nbkts, key); + } + fn remove(key: K) -> option::t<V> { + let i: uint = 0u; + let h: uint = hasher(key); + while i < nbkts { + let j: uint = hash(h, nbkts, i); + alt bkts[j] { + some(k, v) { + let k_ = k; + let vo = option::some(v); + if eqer(key, k_) { + bkts[j] = deleted; + nelts -= 1u; + ret vo; + } + } + deleted. { } + nil. { ret option::none; } + } + i += 1u; + } + ret option::none; + } + fn rehash() { + let newbkts = make_buckets(nbkts); + rehash(hasher, eqer, bkts, nbkts, newbkts, nbkts); + bkts = newbkts; + } + fn items(it: block(K, V)) { + for b in bkts { + alt b { some(k, v) { it(copy k, copy v); } _ { } } + } + } + fn keys(it: block(K)) { + for b in bkts { + alt b { some(k, _) { it(copy k); } _ { } } + } + } + fn values(it: block(V)) { + for b in bkts { + alt b { some(_, v) { it(copy v); } _ { } } + } + } + } + let bkts = make_buckets(initial_capacity); + ret hashmap(hasher, eqer, bkts, initial_capacity, 0u, load_factor); +} + +/* +Function: new_str_hash + +Construct a hashmap for string keys +*/ +fn new_str_hash<copy V>() -> hashmap<str, V> { + ret mk_hashmap(str::hash, str::eq); +} + +/* +Function: new_int_hash + +Construct a hashmap for int keys +*/ +fn new_int_hash<copy V>() -> hashmap<int, V> { + fn hash_int(&&x: int) -> uint { ret x as uint; } + fn eq_int(&&a: int, &&b: int) -> bool { ret a == b; } + ret mk_hashmap(hash_int, eq_int); +} + +/* +Function: new_uint_hash + +Construct a hashmap for uint keys +*/ +fn new_uint_hash<copy V>() -> hashmap<uint, V> { + fn hash_uint(&&x: uint) -> uint { ret x; } + fn eq_uint(&&a: uint, &&b: uint) -> bool { ret a == b; } + ret mk_hashmap(hash_uint, eq_uint); +} + +/* +Function: set_add + +Convenience function for adding keys to a hashmap with nil type keys +*/ +fn set_add<K>(set: hashset<K>, key: K) -> bool { ret set.insert(key, ()); } + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/math.rs b/src/libstd/math.rs new file mode 100644 index 00000000000..72056548ca5 --- /dev/null +++ b/src/libstd/math.rs @@ -0,0 +1,387 @@ +/* + +Module: math + +Floating point operations and constants for `float`s +*/ + +export consts; +export min, max; + +// Currently this module supports from -lmath: +// C95 + log2 + log1p + trunc + round + rint + +export + acos, asin, atan, atan2, ceil, cos, cosh, exp, abs, floor, fmod, frexp, + ldexp, ln, ln1p, log10, log2, modf, rint, round, pow, sin, sinh, sqrt, + tan, tanh, trunc; + +// These two must match in width according to architecture + +import ctypes::m_float; +import ctypes::c_int; +import m_float = math_f64; + +// FIXME replace with redirect to m_float::consts::FOO as soon as it works +mod consts { + /* + Const: pi + + Archimedes' constant + */ + const pi: float = 3.14159265358979323846264338327950288; + + /* + Const: frac_pi_2 + + pi/2.0 + */ + const frac_pi_2: float = 1.57079632679489661923132169163975144; + + /* + Const: frac_pi_4 + + pi/4.0 + */ + const frac_pi_4: float = 0.785398163397448309615660845819875721; + + /* + Const: frac_1_pi + + 1.0/pi + */ + const frac_1_pi: float = 0.318309886183790671537767526745028724; + + /* + Const: frac_2_pi + + 2.0/pi + */ + const frac_2_pi: float = 0.636619772367581343075535053490057448; + + /* + Const: frac_2_sqrtpi + + 2.0/sqrt(pi) + */ + const frac_2_sqrtpi: float = 1.12837916709551257389615890312154517; + + /* + Const: sqrt2 + + sqrt(2.0) + */ + const sqrt2: float = 1.41421356237309504880168872420969808; + + /* + Const: frac_1_sqrt2 + + 1.0/sqrt(2.0) + */ + const frac_1_sqrt2: float = 0.707106781186547524400844362104849039; + + /* + Const: e + + Euler's number + */ + const e: float = 2.71828182845904523536028747135266250; + + /* + Const: log2_e + + log2(e) + */ + const log2_e: float = 1.44269504088896340735992468100189214; + + /* + Const: log10_e + + log10(e) + */ + const log10_e: float = 0.434294481903251827651128918916605082; + + /* + Const: ln_2 + + ln(2.0) + */ + const ln_2: float = 0.693147180559945309417232121458176568; + + /* + Const: ln_10 + + ln(10.0) + */ + const ln_10: float = 2.30258509299404568401799145468436421; +} + + +// FIXME min/max type specialize via libm when overloading works +// (in theory fmax/fmin, fmaxf, fminf /should/ be faster) + +/* +Function: min + +Returns the minimum of two values +*/ +pure fn min<copy T>(x: T, y: T) -> T { x < y ? x : y } + +/* +Function: max + +Returns the maximum of two values +*/ +pure fn max<copy T>(x: T, y: T) -> T { x < y ? y : x } + +/* +Function: acos + +Returns the arccosine of an angle (measured in rad) +*/ +pure fn acos(x: float) -> float + { m_float::acos(x as m_float) as float } + +/* +Function: asin + +Returns the arcsine of an angle (measured in rad) +*/ +pure fn asin(x: float) -> float + { m_float::asin(x as m_float) as float } + +/* +Function: atan + +Returns the arctangents of an angle (measured in rad) +*/ +pure fn atan(x: float) -> float + { m_float::atan(x as m_float) as float } + + +/* +Function: atan2 + +Returns the arctangent of an angle (measured in rad) +*/ +pure fn atan2(y: float, x: float) -> float + { m_float::atan2(y as m_float, x as m_float) as float } + +/* +Function: ceil + +Returns the smallest integral value less than or equal to `n` +*/ +pure fn ceil(n: float) -> float + { m_float::ceil(n as m_float) as float } + +/* +Function: cos + +Returns the cosine of an angle `x` (measured in rad) +*/ +pure fn cos(x: float) -> float + { m_float::cos(x as m_float) as float } + +/* +Function: cosh + +Returns the hyperbolic cosine of `x` + +*/ +pure fn cosh(x: float) -> float + { m_float::cosh(x as m_float) as float } + + +/* +Function: exp + +Returns `consts::e` to the power of `n* +*/ +pure fn exp(n: float) -> float + { m_float::exp(n as m_float) as float } + +/* +Function: abs + +Returns the absolute value of `n` +*/ +pure fn abs(n: float) -> float + { m_float::abs(n as m_float) as float } + +/* +Function: floor + +Returns the largest integral value less than or equal to `n` +*/ +pure fn floor(n: float) -> float + { m_float::floor(n as m_float) as float } + +/* +Function: fmod + +Returns the floating-point remainder of `x/y` +*/ +pure fn fmod(x: float, y: float) -> float + { m_float::fmod(x as m_float, y as m_float) as float } + +/* +Function: ln + +Returns the natural logaritm of `n` +*/ +pure fn ln(n: float) -> float + { m_float::ln(n as m_float) as float } + +/* +Function: ldexp + +Returns `x` multiplied by 2 to the power of `n` +*/ +pure fn ldexp(n: float, i: int) -> float + { m_float::ldexp(n as m_float, i as c_int) as float } + +/* +Function: ln1p + +Returns the natural logarithm of `1+n` accurately, +even for very small values of `n` +*/ +pure fn ln1p(n: float) -> float + { m_float::ln1p(n as m_float) as float } + +/* +Function: log10 + +Returns the logarithm to base 10 of `n` +*/ +pure fn log10(n: float) -> float + { m_float::log10(n as m_float) as float } + +/* +Function: log2 + +Returns the logarithm to base 2 of `n` +*/ +pure fn log2(n: float) -> float + { m_float::log2(n as m_float) as float } + + +/* +Function: modf + +Breaks `n` into integral and fractional parts such that both +have the same sign as `n` + +The integral part is stored in `iptr`. + +Returns: + +The fractional part of `n` +*/ +pure fn modf(n: float, &iptr: float) -> float { + unchecked { + let f = iptr as m_float; + let r = m_float::modf(n as m_float, f) as float; + iptr = f as float; + ret r; + } +} + +/* +Function: frexp + +Breaks `n` into a normalized fraction and an integral power of 2 + +The inegral part is stored in iptr. + +The functions return a number x such that x has a magnitude in the interval +[1/2, 1) or 0, and `n == x*(2 to the power of exp)`. + +Returns: + +The fractional part of `n` +*/ +pure fn frexp(n: float, &exp: c_int) -> float + { m_float::frexp(n as m_float, exp) as float } + +/* +Function: pow +*/ +pure fn pow(v: float, e: float) -> float + { m_float::pow(v as m_float, e as m_float) as float } + + +/* +Function: rint + +Returns the integral value nearest to `x` (according to the +prevailing rounding mode) in floating-point format +*/ +pure fn rint(x: float) -> float + { m_float::rint(x as m_float) as float } + +/* +Function: round + + +Return the integral value nearest to `x` rounding half-way +cases away from zero, regardless of the current rounding direction. +*/ +pure fn round(x: float) -> float + { m_float::round(x as m_float) as float } + +/* +Function: sin + +Returns the sine of an angle `x` (measured in rad) +*/ +pure fn sin(x: float) -> float + { m_float::sin(x as m_float) as float } + +/* +Function: sinh + +Returns the hyperbolic sine of an angle `x` (measured in rad) +*/ +pure fn sinh(x: float) -> float + { m_float::sinh(x as m_float) as float } + +/* +Function: sqrt + +Returns the square root of `x` +*/ +pure fn sqrt(x: float) -> float + { m_float::sqrt(x as m_float) as float } + +/* +Function: tan + +Returns the tangent of an angle `x` (measured in rad) + +*/ +pure fn tan(x: float) -> float + { m_float::tan(x as m_float) as float } + +/* +Function: tanh + +Returns the hyperbolic tangent of an angle `x` (measured in rad) + +*/ +pure fn tanh(x: float) -> float + { m_float::tanh(x as m_float) as float } + +/* +Function: trunc + +Returns the integral value nearest to but no larger in magnitude than `x` + +*/ +pure fn trunc(x: float) -> float + { m_float::trunc(x as m_float) as float } + + + + diff --git a/src/libstd/math_f32.rs b/src/libstd/math_f32.rs new file mode 100644 index 00000000000..2172c9f8a99 --- /dev/null +++ b/src/libstd/math_f32.rs @@ -0,0 +1,112 @@ + +/* +Module: math_f32 + +Floating point operations and constants for `f32` + +This exposes the same operations as `math`, just for `f32` even though +they do not show up in the docs right now! +*/ + +import cmath::f32::*; + +export + acos, asin, atan, atan2, ceil, cos, cosh, exp, abs, floor, fmod, + frexp, ldexp, ln, ln1p, log10, log2, modf, rint, round, pow, sin, + sinh, sqrt, tan, tanh, trunc; + +export consts; + +mod consts { + + /* + Const: pi + + Archimedes' constant + */ + const pi: f32 = 3.14159265358979323846264338327950288f32; + + /* + Const: frac_pi_2 + + pi/2.0 + */ + const frac_pi_2: f32 = 1.57079632679489661923132169163975144f32; + + /* + Const: frac_pi_4 + + pi/4.0 + */ + const frac_pi_4: f32 = 0.785398163397448309615660845819875721f32; + + /* + Const: frac_1_pi + + 1.0/pi + */ + const frac_1_pi: f32 = 0.318309886183790671537767526745028724f32; + + /* + Const: frac_2_pi + + 2.0/pi + */ + const frac_2_pi: f32 = 0.636619772367581343075535053490057448f32; + + /* + Const: frac_2_sqrtpi + + 2.0/sqrt(pi) + */ + const frac_2_sqrtpi: f32 = 1.12837916709551257389615890312154517f32; + + /* + Const: sqrt2 + + sqrt(2.0) + */ + const sqrt2: f32 = 1.41421356237309504880168872420969808f32; + + /* + Const: frac_1_sqrt2 + + 1.0/sqrt(2.0) + */ + const frac_1_sqrt2: f32 = 0.707106781186547524400844362104849039f32; + + /* + Const: e + + Euler's number + */ + const e: f32 = 2.71828182845904523536028747135266250f32; + + /* + Const: log2_e + + log2(e) + */ + const log2_e: f32 = 1.44269504088896340735992468100189214f32; + + /* + Const: log10_e + + log10(e) + */ + const log10_e: f32 = 0.434294481903251827651128918916605082f32; + + /* + Const: ln_2 + + ln(2.0) + */ + const ln_2: f32 = 0.693147180559945309417232121458176568f32; + + /* + Const: ln_10 + + ln(10.0) + */ + const ln_10: f32 = 2.30258509299404568401799145468436421f32; +} \ No newline at end of file diff --git a/src/libstd/math_f64.rs b/src/libstd/math_f64.rs new file mode 100644 index 00000000000..639dc4a29b2 --- /dev/null +++ b/src/libstd/math_f64.rs @@ -0,0 +1,112 @@ + +/* +Module: math_f64 + +Floating point operations and constants for `f64`s + +This exposes the same operations as `math`, just for `f64` even though +they do not show up in the docs right now! +*/ + +import cmath::f64::*; + +export + acos, asin, atan, atan2, ceil, cos, cosh, exp, abs, floor, fmod, + frexp, ldexp, ln, ln1p, log10, log2, modf, rint, round, pow, sin, + sinh, sqrt, tan, tanh, trunc; + +export consts; + +mod consts { + + /* + Const: pi + + Archimedes' constant + */ + const pi: f64 = 3.14159265358979323846264338327950288f64; + + /* + Const: frac_pi_2 + + pi/2.0 + */ + const frac_pi_2: f64 = 1.57079632679489661923132169163975144f64; + + /* + Const: frac_pi_4 + + pi/4.0 + */ + const frac_pi_4: f64 = 0.785398163397448309615660845819875721f64; + + /* + Const: frac_1_pi + + 1.0/pi + */ + const frac_1_pi: f64 = 0.318309886183790671537767526745028724f64; + + /* + Const: frac_2_pi + + 2.0/pi + */ + const frac_2_pi: f64 = 0.636619772367581343075535053490057448f64; + + /* + Const: frac_2_sqrtpi + + 2.0/sqrt(pi) + */ + const frac_2_sqrtpi: f64 = 1.12837916709551257389615890312154517f64; + + /* + Const: sqrt2 + + sqrt(2.0) + */ + const sqrt2: f64 = 1.41421356237309504880168872420969808f64; + + /* + Const: frac_1_sqrt2 + + 1.0/sqrt(2.0) + */ + const frac_1_sqrt2: f64 = 0.707106781186547524400844362104849039f64; + + /* + Const: e + + Euler's number + */ + const e: f64 = 2.71828182845904523536028747135266250f64; + + /* + Const: log2_e + + log2(e) + */ + const log2_e: f64 = 1.44269504088896340735992468100189214f64; + + /* + Const: log10_e + + log10(e) + */ + const log10_e: f64 = 0.434294481903251827651128918916605082f64; + + /* + Const: ln_2 + + ln(2.0) + */ + const ln_2: f64 = 0.693147180559945309417232121458176568f64; + + /* + Const: ln_10 + + ln(10.0) + */ + const ln_10: f64 = 2.30258509299404568401799145468436421f64; +} \ No newline at end of file diff --git a/src/libstd/net.rs b/src/libstd/net.rs new file mode 100644 index 00000000000..5ee7cfa602c --- /dev/null +++ b/src/libstd/net.rs @@ -0,0 +1,56 @@ +/* +Module: net +*/ + +import vec; +import uint; + +/* Section: Types */ + +/* +Tag: ip_addr + +An IP address +*/ +tag ip_addr { + /* + Variant: ipv4 + + An IPv4 address + */ + ipv4(u8, u8, u8, u8); +} + +/* Section: Operations */ + +/* +Function: format_addr + +Convert an <ip_addr> to a str +*/ +fn format_addr(ip: ip_addr) -> str { + alt ip { + ipv4(a, b, c, d) { + #fmt["%u.%u.%u.%u", a as uint, b as uint, c as uint, d as uint] + } + _ { fail "Unsupported address type"; } + } +} + +/* +Function: parse_addr + +Convert a str to <ip_addr> + +Converts a string of the format "x.x.x.x" into an ip_addr tag. + +Failure: + +String must be a valid IPv4 address +*/ +fn parse_addr(ip: str) -> ip_addr { + let parts = vec::map({|s| uint::from_str(s) }, str::split(ip, "."[0])); + if vec::len(parts) != 4u { fail "Too many dots in IP address"; } + for i in parts { if i > 255u { fail "Invalid IP Address part."; } } + ipv4(parts[0] as u8, parts[1] as u8, parts[2] as u8, parts[3] as u8) +} diff --git a/src/libstd/option.rs b/src/libstd/option.rs new file mode 100644 index 00000000000..403cb47f47e --- /dev/null +++ b/src/libstd/option.rs @@ -0,0 +1,93 @@ +/* +Module: option + +Represents the presence or absence of a value. + +Every option<T> value can either be some(T) or none. Where in other languages +you might use a nullable type, in Rust you would use an option type. +*/ + +/* +Tag: t + +The option type +*/ +tag t<T> { + /* Variant: none */ + none; + /* Variant: some */ + some(T); +} + +/* Section: Operations */ + +/* +Function: get + +Gets the value out of an option + +Failure: + +Fails if the value equals `none`. +*/ +fn get<copy T>(opt: t<T>) -> T { + alt opt { some(x) { ret x; } none. { fail "option none"; } } +} + +/* +*/ +fn map<T, U>(f: block(T) -> U, opt: t<T>) -> t<U> { + alt opt { some(x) { some(f(x)) } none. { none } } +} + +/* +Function: is_none + +Returns true if the option equals none +*/ +pure fn is_none<T>(opt: t<T>) -> bool { + alt opt { none. { true } some(_) { false } } +} + +/* +Function: is_some + +Returns true if the option contains some value +*/ +pure fn is_some<T>(opt: t<T>) -> bool { !is_none(opt) } + +/* +Function: from_maybe + +Returns the contained value or a default +*/ +fn from_maybe<T>(def: T, opt: t<T>) -> T { + alt opt { some(x) { x } none. { def } } +} + +/* +Function: maybe + +Applies a function to the contained value or returns a default +*/ +fn maybe<T, U>(def: U, f: block(T) -> U, opt: t<T>) -> U { + alt opt { none. { def } some(t) { f(t) } } +} + +// FIXME: Can be defined in terms of the above when/if we have const bind. +/* +Function: may + +Performs an operation on the contained value or does nothing +*/ +fn may<T>(f: block(T), opt: t<T>) { + alt opt { none. {/* nothing */ } some(t) { f(t); } } +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/posix_fs.rs b/src/libstd/posix_fs.rs new file mode 100644 index 00000000000..befa4c9829c --- /dev/null +++ b/src/libstd/posix_fs.rs @@ -0,0 +1,45 @@ +#[abi = "cdecl"] +native mod rustrt { + fn rust_list_files(path: str) -> [str]; +} + +fn list_dir(path: str) -> [str] { + ret rustrt::rust_list_files(path); + + // FIXME: No idea why, but this appears to corrupt memory on OSX. I + // suspect it has to do with the tasking primitives somehow, or perhaps + // the FFI. Worth investigating more when we're digging into the FFI and + // unsafe mode in more detail; in the meantime we just call list_files + // above and skip this code. + + /* + auto dir = os::libc::opendir(str::buf(path)); + assert (dir as uint != 0u); + let vec<str> result = []; + while (true) { + auto ent = os::libc::readdir(dir); + if (ent as int == 0) { + os::libc::closedir(dir); + ret result; + } + vec::push::<str>(result, rustrt::rust_dirent_filename(ent)); + } + os::libc::closedir(dir); + ret result; + */ + +} + +fn path_is_absolute(p: str) -> bool { ret str::char_at(p, 0u) == '/'; } + +const path_sep: char = '/'; + +const alt_path_sep: char = '/'; + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/ptr.rs b/src/libstd/ptr.rs new file mode 100644 index 00000000000..0372b17cdf1 --- /dev/null +++ b/src/libstd/ptr.rs @@ -0,0 +1,52 @@ +/* +Module: ptr + +Unsafe pointer utility functions +*/ +#[abi = "rust-intrinsic"] +native mod rusti { + fn addr_of<T>(val: T) -> *T; + fn ptr_offset<T>(ptr: *T, count: uint) -> *T; +} + +/* +Function: addr_of + +Get an unsafe pointer to a value +*/ +fn addr_of<T>(val: T) -> *T { ret rusti::addr_of(val); } + +/* +Function: mut_addr_of + +Get an unsafe mutable pointer to a value +*/ +fn mut_addr_of<T>(val: T) -> *mutable T unsafe { + ret unsafe::reinterpret_cast(rusti::addr_of(val)); +} + +/* +Function: offset + +Calculate the offset from a pointer +*/ +fn offset<T>(ptr: *T, count: uint) -> *T { + ret rusti::ptr_offset(ptr, count); +} + +/* +Function: mut_offset + +Calculate the offset from a mutable pointer +*/ +fn mut_offset<T>(ptr: *mutable T, count: uint) -> *mutable T { + ret rusti::ptr_offset(ptr as *T, count) as *mutable T; +} + + +/* +Function: null + +Create an unsafe null pointer +*/ +fn null<T>() -> *T unsafe { ret unsafe::reinterpret_cast(0u); } diff --git a/src/libstd/rand.rs b/src/libstd/rand.rs new file mode 100644 index 00000000000..be350baadbe --- /dev/null +++ b/src/libstd/rand.rs @@ -0,0 +1,86 @@ +/* +Module: rand + +Random number generation +*/ +#[abi = "cdecl"] +native mod rustrt { + type rctx; + fn rand_new() -> rctx; + fn rand_next(c: rctx) -> u32; + fn rand_free(c: rctx); +} + +/* Section: Types */ + +/* +Obj: rng + +A random number generator +*/ +type rng = obj { + /* + Method: next + + Return the next random integer + */ + fn next() -> u32; + + /* + Method: next_float + + Return the next random float + */ + fn next_float() -> float; + + /* + Method: gen_str + + Return a random string composed of A-Z, a-z, 0-9. + */ + fn gen_str(len: uint) -> str; +}; + +resource rand_res(c: rustrt::rctx) { rustrt::rand_free(c); } + +/* Section: Operations */ + +/* +Function: mk_rng + +Create a random number generator +*/ +fn mk_rng() -> rng { + obj rt_rng(c: @rand_res) { + fn next() -> u32 { ret rustrt::rand_next(**c); } + fn next_float() -> float { + let u1 = rustrt::rand_next(**c) as float; + let u2 = rustrt::rand_next(**c) as float; + let u3 = rustrt::rand_next(**c) as float; + let scale = u32::max_value as float; + ret ((u1 / scale + u2) / scale + u3) / scale; + } + fn gen_str(len: uint) -> str { + let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz" + + "0123456789"; + let s = ""; + let i = 0u; + while (i < len) { + let n = rustrt::rand_next(**c) as uint % + str::char_len(charset); + s = s + str::from_char(str::char_at(charset, n)); + i += 1u; + } + s + } + } + ret rt_rng(@rand_res(rustrt::rand_new())); +} +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/result.rs b/src/libstd/result.rs new file mode 100644 index 00000000000..550f53470bb --- /dev/null +++ b/src/libstd/result.rs @@ -0,0 +1,112 @@ +/* +Module: result + +A type representing either success or failure +*/ + +/* Section: Types */ + +/* +Tag: t + +The result type +*/ +tag t<T, U> { + /* + Variant: ok + + Contains the result value + */ + ok(T); + /* + Variant: err + + Contains the error value + */ + err(U); +} + +/* Section: Operations */ + +/* +Function: get + +Get the value out of a successful result + +Failure: + +If the result is an error +*/ +fn get<T, U>(res: t<T, U>) -> T { + alt res { + ok(t) { t } + err(_) { + // FIXME: Serialize the error value + // and include it in the fail message + fail "get called on error result"; + } + } +} + +/* +Function: get_err + +Get the value out of an error result + +Failure: + +If the result is not an error +*/ +fn get_err<T, U>(res: t<T, U>) -> U { + alt res { + err(u) { u } + ok(_) { + fail "get_error called on ok result"; + } + } +} + +/* +Function: success + +Returns true if the result is <ok> +*/ +fn success<T, U>(res: t<T, U>) -> bool { + alt res { + ok(_) { true } + err(_) { false } + } +} + +/* +Function: failure + +Returns true if the result is <error> +*/ +fn failure<T, U>(res: t<T, U>) -> bool { + !success(res) +} + +/* +Function: chain + +Call a function based on a previous result + +If `res` is <ok> then the value is extracted and passed to `op` whereupon +`op`s result is returned. if `res` is <err> then it is immediately returned. +This function can be used to compose the results of two functions. + +Example: + +> let res = chain(read_file(file), { |buf| +> ok(parse_buf(buf)) +> }) + +*/ +fn chain<T, copy U, copy V>(res: t<T, V>, op: block(T) -> t<U, V>) + -> t<U, V> { + alt res { + ok(t) { op(t) } + err(e) { err(e) } + } +} diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs new file mode 100644 index 00000000000..19819a968bf --- /dev/null +++ b/src/libstd/rope.rs @@ -0,0 +1,1337 @@ +/* +Module: rope + + +High-level text containers. + +Ropes are a high-level representation of text that offers +much better performance than strings for common operations, +and generally reduce memory allocations and copies, while only +entailing a small degradation of less common operations. + +More precisely, where a string is represented as a memory buffer, +a rope is a tree structure whose leaves are slices of immutable +strings. Therefore, concatenation, appending, prepending, substrings, +etc. are operations that require only trivial tree manipulation, +generally without having to copy memory. In addition, the tree +structure of ropes makes them suitable as a form of index to speed-up +access to Unicode characters by index in long chunks of text. + +The following operations are algorithmically faster in ropes: +- extracting a subrope is logarithmic (linear in strings); +- appending/prepending is near-constant time (linear in strings); +- concatenation is near-constant time (linear in strings); +- char length is constant-time (linear in strings); +- access to a character by index is logarithmic (linear in strings); + */ + + + + +/* + Type: rope + + The type of ropes. + */ +type rope = node::root; + +/* + Section: Creating a rope + */ + +/* + Function:empty + + Create an empty rope + */ +fn empty() -> rope { + ret node::empty; +} + +/* + Function: of_str + + Adopt a string as a rope. + + Parameters: + +str - A valid string. + + Returns: + +A rope representing the same string as `str`. Depending of the length +of `str`, this rope may be empty, flat or complex. + +Performance notes: +- this operation does not copy the string; +- the function runs in linear time. + */ +fn of_str(str: @str) -> rope { + ret of_substr(str, 0u, str::byte_len(*str)); +} + +/* +Function: of_substr + +As `of_str` but for a substring. + +Performance note: +- this operation does not copy the substring. + +Parameters: + +byte_offset - The offset of `str` at which the rope starts. +byte_len - The number of bytes of `str` to use. + +Returns: + +A rope representing the same string as +`str::substr(str, byte_offset, byte_len)`. +Depending on `byte_len`, this rope may be empty, flat or complex. + +Safety notes: +- this function does _not_ check the validity of the substring; +- this function fails if `byte_offset` or `byte_len` do not match `str`. + */ +fn of_substr(str: @str, byte_offset: uint, byte_len: uint) -> rope { + if byte_len == 0u { ret node::empty; } + if byte_offset + byte_len > str::byte_len(*str) { fail; } + ret node::content(node::of_substr(str, byte_offset, byte_len)); +} + +/* +Section: Adding things to a rope + */ + +/* +Function: append_char + +Add one char to the end of the rope + +Performance note: +- this function executes in near-constant time + */ +fn append_char(rope: rope, char: char) -> rope { + ret append_str(rope, @str::from_chars([char])); +} + +/* +Function: append_str + +Add one string to the end of the rope + +Performance note: +- this function executes in near-linear time + */ +fn append_str(rope: rope, str: @str) -> rope { + ret append_rope(rope, of_str(str)) +} + +/* +Function: prepend_char + +Add one char to the beginning of the rope + +Performance note: +- this function executes in near-constant time + */ +fn prepend_char(rope: rope, char: char) -> rope { + ret prepend_str(rope, @str::from_chars([char])); +} + +/* +Function: prepend_str + +Add one string to the beginning of the rope + +Performance note: +- this function executes in near-linear time + */ +fn prepend_str(rope: rope, str: @str) -> rope { + ret append_rope(of_str(str), rope) +} + +/* +Function: append_rope + +Concatenate two ropes + */ +fn append_rope(left: rope, right: rope) -> rope { + alt(left) { + node::empty. { ret right; } + node::content(left_content) { + alt(right) { + node::empty. { ret left; } + node::content(right_content) { + ret node::content(node::concat2(left_content, right_content)); + } + } + } + } +} + +/* +Function: concat + +Concatenate many ropes. + +If the ropes are balanced initially and have the same height, the resulting +rope remains balanced. However, this function does not take any further +measure to ensure that the result is balanced. + */ +fn concat(v: [rope]) -> rope { + //Copy `v` into a mutable vector + let len = vec::len(v); + if len == 0u { ret node::empty; } + let ropes = vec::init_elt_mut(v[0], len); + uint::range(1u, len) {|i| + ropes[i] = v[i]; + } + + //Merge progresively + while len > 1u { + uint::range(0u, len/2u) {|i| + ropes[i] = append_rope(ropes[2u*i], ropes[2u*i+1u]); + } + if len%2u != 0u { + ropes[len/2u] = ropes[len - 1u]; + len = len/2u + 1u; + } else { + len = len/2u; + } + } + + //Return final rope + ret ropes[0]; +} + + +/* +Section: Keeping ropes healthy + */ + + + +/* +Function: bal + +Balance a rope. + +Returns: + +A copy of the rope in which small nodes have been grouped in memory, +and with a reduced height. + +If you perform numerous rope concatenations, it is generally a good idea +to rebalance your rope at some point, before using it for other purposes. + */ +fn bal(rope:rope) -> rope { + alt(rope) { + node::empty. { ret rope } + node::content(x) { + alt(node::bal(x)) { + option::none. { rope } + option::some(y) { node::content(y) } + } + } + } +} + +/* +Section: Transforming ropes + */ + + +/* +Function: sub_chars + +Extract a subrope from a rope. + +Performance note: +- on a balanced rope, this operation takes algorithmic time; +- this operation does not involve any copying + +Safety note: +- this function fails if char_offset/char_len do not represent +valid positions in rope + */ +fn sub_chars(rope: rope, char_offset: uint, char_len: uint) -> rope { + if char_len == 0u { ret node::empty; } + alt(rope) { + node::empty. { fail } + node::content(node) { + if char_len > node::char_len(node) { fail } + else { + ret node::content(node::sub_chars(node, char_offset, char_len)) + } + } + } +} + +/* +Function:sub_bytes + +Extract a subrope from a rope. + +Performance note: +- on a balanced rope, this operation takes algorithmic time; +- this operation does not involve any copying + +Safety note: +- this function fails if byte_offset/byte_len do not represent +valid positions in rope + */ +fn sub_bytes(rope: rope, byte_offset: uint, byte_len: uint) -> rope { + if byte_len == 0u { ret node::empty; } + alt(rope) { + node::empty. { fail } + node::content(node) { + if byte_len > node::byte_len(node) { fail } + else { + ret node::content(node::sub_bytes(node, byte_offset, byte_len)) + } + } + } +} + +/* +Section: Comparing ropes + */ + +/* +Function: cmp + +Compare two ropes by Unicode lexicographical order. + +This function compares only the contents of the rope, not their structure. + +Returns: + +A negative value if `left < right`, 0 if eq(left, right) or a positive +value if `left > right` + */ +fn cmp(left: rope, right: rope) -> int { + alt((left, right)) { + (node::empty., node::empty.) { ret 0; } + (node::empty., _) { ret -1;} + (_, node::empty.) { ret 1;} + (node::content(a), node::content(b)) { + ret node::cmp(a, b); + } + } +} + +/* +Function: eq + +Returns: + + `true` if both ropes have the same content (regardless of their structure), +`false` otherwise +*/ +fn eq(left: rope, right: rope) -> bool { + ret cmp(left, right) == 0; +} + +/* +Function: le + +Parameters + left - an arbitrary rope + right - an arbitrary rope + +Returns: + + `true` if `left <= right` in lexicographical order (regardless of their +structure), `false` otherwise +*/ +fn le(left: rope, right: rope) -> bool { + ret cmp(left, right) <= 0; +} + +/* +Function: lt + +Parameters + left - an arbitrary rope + right - an arbitrary rope + +Returns: + + `true` if `left < right` in lexicographical order (regardless of their +structure), `false` otherwise +*/ +fn lt(left: rope, right: rope) -> bool { + ret cmp(left, right) < 0; +} + +/* +Function: ge + +Parameters + left - an arbitrary rope + right - an arbitrary rope + +Returns: + + `true` if `left >= right` in lexicographical order (regardless of their +structure), `false` otherwise +*/ +fn ge(left: rope, right: rope) -> bool { + ret cmp(left, right) >= 0; +} + +/* +Function: gt + +Parameters + left - an arbitrary rope + right - an arbitrary rope + +Returns: + + `true` if `left > right` in lexicographical order (regardless of their +structure), `false` otherwise +*/ +fn gt(left: rope, right: rope) -> bool { + ret cmp(left, right) > 0; +} + +/* +Section: Iterating + */ + +/* +Function: loop_chars + +Loop through a rope, char by char + +While other mechanisms are available, this is generally the best manner +of looping through the contents of a rope char by char. If you prefer a +loop that iterates through the contents string by string (e.g. to print +the contents of the rope or output it to the system), however, +you should rather use `traverse_components`. + +Parameters: +rope - A rope to traverse. It may be empty. +it - A block to execute with each consecutive character of the rope. +Return `true` to continue, `false` to stop. + +Returns: + +`true` If execution proceeded correctly, `false` if it was interrupted, +that is if `it` returned `false` at any point. + */ +fn loop_chars(rope: rope, it: block(char) -> bool) -> bool { + alt(rope) { + node::empty. { ret true } + node::content(x) { ret node::loop_chars(x, it) } + } +} + +/* +Function: iter_chars + +Loop through a rope, char by char, until the end. + +Parameters: +rope - A rope to traverse. It may be empty. +it - A block to execute with each consecutive character of the rope. + */ +fn iter_chars(rope: rope, it: block(char)) { + loop_chars(rope) {|x| + it(x); + ret true + } +} + +/* +Function: loop_leaves + +Loop through a rope, string by string + +While other mechanisms are available, this is generally the best manner of +looping through the contents of a rope string by string, which may be useful +e.g. to print strings as you see them (without having to copy their +contents into a new string), to send them to then network, to write them to +a file, etc.. If you prefer a loop that iterates through the contents +char by char (e.g. to search for a char), however, you should rather +use `traverse`. + +Parameters: + +rope - A rope to traverse. It may be empty. +it - A block to execute with each consecutive string component of the rope. +Return `true` to continue, `false` to stop. + +Returns: + +`true` If execution proceeded correctly, `false` if it was interrupted, +that is if `it` returned `false` at any point. + */ +fn loop_leaves(rope: rope, it: block(node::leaf) -> bool) -> bool{ + alt(rope) { + node::empty. { ret true } + node::content(x) {ret node::loop_leaves(x, it)} + } +} + +mod iterator { + mod leaf { + fn start(rope: rope) -> node::leaf_iterator::t { + alt(rope) { + node::empty. { ret node::leaf_iterator::empty() } + node::content(x) { ret node::leaf_iterator::start(x) } + } + } + fn next(it: node::leaf_iterator::t) -> option::t<node::leaf> { + ret node::leaf_iterator::next(it); + } + } + mod char { + fn start(rope: rope) -> node::char_iterator::t { + alt(rope) { + node::empty. { ret node::char_iterator::empty() } + node::content(x) { ret node::char_iterator::start(x) } + } + } + fn next(it: node::char_iterator::t) -> option::t<char> { + ret node::char_iterator::next(it) + } + } +} + +/* + Section: Rope properties + */ + +/* + Function: height + + Returns: The height of the rope, i.e. a bound on the number of +operations which must be performed during a character access before +finding the leaf in which a character is contained. + + Performance note: Constant time. +*/ +fn height(rope: rope) -> uint { + alt(rope) { + node::empty. { ret 0u; } + node::content(x) { ret node::height(x); } + } +} + + + +/* + Function: char_len + + Returns: The number of character in the rope + + Performance note: Constant time. + */ +pure fn char_len(rope: rope) -> uint { + alt(rope) { + node::empty. { ret 0u; } + node::content(x) { ret node::char_len(x) } + } +} + +/* + Function: char_len + + Returns: The number of bytes in the rope + + Performance note: Constant time. + */ +pure fn byte_len(rope: rope) -> uint { + alt(rope) { + node::empty. { ret 0u; } + node::content(x) { ret node::byte_len(x) } + } +} + +/* + Function: char_at + + Parameters: + pos - A position in the rope + + Returns: The character at position `pos` + + Safety notes: The function will fail if `pos` + is not a valid position in the rope. + + Performance note: This function executes in a time + proportional to the height of the rope + the (bounded) + length of the largest leaf. + */ +fn char_at(rope: rope, pos: uint) -> char { + alt(rope) { + node::empty. { fail } + node::content(x) { ret node::char_at(x, pos) } + } +} + + +/* + Section: Implementation +*/ +mod node { + + /* + Enum: node::root + + Implementation of type `rope` + + Constants: + empty - An empty rope + content - A non-empty rope + */ + tag root { + empty; + content(@node); + } + + /* + Struct: node::leaf + + A text component in a rope. + + This is actually a slice in a rope, so as to ensure maximal sharing. + */ + type leaf = { + + /* + Field: byte_offset + + The number of bytes skipped in `content` + */ + byte_offset: uint, + + /* + Field: byte_len + + The number of bytes of `content` to use + */ + byte_len: uint, + + /* + Field: char_len + + + The number of chars in the leaf. + */ + char_len: uint, + + /* + Field: content + + Contents of the leaf. + + Note that we can have `char_len < str::char_len(content)`, if this + leaf is only a subset of the string. Also note that the string + can be shared between several ropes, e.g. for indexing purposes. + */ + content: @str + }; + + + /* + Struct node::concat + + A node obtained from the concatenation of two other nodes + */ + type concat = { + + /* + Field: left + + The node containing the beginning of the text. + */ + left: @node,//TODO: Perhaps a `vec` instead of `left`/`right` + + /* + Field: right + + The node containing the end of the text. + */ + right: @node, + + /* + Field: char_len + + The number of chars contained in all leaves of this node. + */ + char_len: uint, + + /* + Field: byte_len + + The number of bytes in the subrope. + + Used to pre-allocate the correct amount of storage for serialization. + */ + byte_len: uint, + + /* + Field: height + + Height of the subrope. + + Used for rebalancing and to allocate stacks for + traversals. + */ + height: uint + }; + + /* + Enum: node::node + + leaf - A leaf consisting in a `str` + concat - The concatenation of two ropes + */ + tag node { + leaf(leaf); + concat(concat); + } + + /* + The maximal number of chars that _should_ be permitted in a single node. + + This is not a strict value + */ + const hint_max_leaf_char_len: uint = 256u; + + /* + The maximal height that _should_ be permitted in a tree. + + This is not a strict value + */ + const hint_max_node_height: uint = 16u; + + /* + Function: of_str + + Adopt a string as a node. + + If the string is longer than `max_leaf_char_len`, it is + logically split between as many leaves as necessary. Regardless, + the string itself is not copied. + + Performance note: The complexity of this function is linear in + the length of `str`. + */ + fn of_str(str: @str) -> @node { + ret of_substr(str, 0u, str::byte_len(*str)); + } + + /* + Function: of_substr + + Adopt a slice of a string as a node. + + If the slice is longer than `max_leaf_char_len`, it is logically split + between as many leaves as necessary. Regardless, the string itself + is not copied. + + Parameters: + byte_start - The byte offset where the slice of `str` starts. + byte_len - The number of bytes from `str` to use. + + Safety note: + - Behavior is undefined if `byte_start` or `byte_len` do not represent + valid positions in `str` + */ + fn of_substr(str: @str, byte_start: uint, byte_len: uint) -> @node { + ret of_substr_unsafer(str, byte_start, byte_len, + str::char_len_range(*str, byte_start, byte_len)); + } + + /* + Function: of_substr_unsafer + + Adopt a slice of a string as a node. + + If the slice is longer than `max_leaf_char_len`, it is logically split + between as many leaves as necessary. Regardless, the string itself + is not copied. + + byte_start - The byte offset where the slice of `str` starts. + byte_len - The number of bytes from `str` to use. + char_len - The number of chars in `str` in the interval + [byte_start, byte_start+byte_len( + + Safety note: + - Behavior is undefined if `byte_start` or `byte_len` do not represent + valid positions in `str` + - Behavior is undefined if `char_len` does not accurately represent the + number of chars between byte_start and byte_start+byte_len + */ + fn of_substr_unsafer(str: @str, byte_start: uint, byte_len: uint, + char_len: uint) -> @node { + assert(byte_start + byte_len <= str::byte_len(*str)); + let candidate = @leaf({ + byte_offset: byte_start, + byte_len: byte_len, + char_len: char_len, + content: str}); + if char_len <= hint_max_leaf_char_len { + ret candidate; + } else { + //Firstly, split `str` in slices of hint_max_leaf_char_len + let leaves = uint::div_ceil(char_len, hint_max_leaf_char_len); + //Number of leaves + let nodes = vec::init_elt_mut(candidate, leaves); + + let i = 0u; + let offset = byte_start; + let first_leaf_char_len = + if char_len%hint_max_leaf_char_len == 0u { + hint_max_leaf_char_len + } else { + char_len%hint_max_leaf_char_len + }; + while i < leaves { + let chunk_char_len: uint = + if i == 0u { first_leaf_char_len } + else { hint_max_leaf_char_len }; + let chunk_byte_len = + str::byte_len_range(*str, offset, chunk_char_len); + nodes[i] = @leaf({ + byte_offset: offset, + byte_len: chunk_byte_len, + char_len: chunk_char_len, + content: str + }); + + offset += chunk_byte_len; + i += 1u; + } + + //Then, build a tree from these slices by collapsing them + while leaves > 1u { + i = 0u; + while i < leaves - 1u {//Concat nodes 0 with 1, 2 with 3 etc. + nodes[i/2u] = concat2(nodes[i], nodes[i + 1u]); + i += 2u; + } + if i == leaves - 1u { + //And don't forget the last node if it is in even position + nodes[i/2u] = nodes[i]; + } + leaves = uint::div_ceil(leaves, 2u); + } + ret nodes[0u]; + } + } + + pure fn byte_len(node: @node) -> uint { + alt(*node) {//TODO: Could we do this without the pattern-matching? + leaf(y) { ret y.byte_len; } + concat(y){ ret y.byte_len; } + } + } + + pure fn char_len(node: @node) -> uint { + alt(*node) { + leaf(y) { ret y.char_len; } + concat(y) { ret y.char_len; } + } + } + + + /* + Function: tree_from_forest_destructive + + Concatenate a forest of nodes into one tree. + + Parameters: + forest - The forest. This vector is progressively rewritten during + execution and should be discarded as meaningless afterwards. + */ + fn tree_from_forest_destructive(forest: [mutable @node]) -> @node { + let i = 0u; + let len = vec::len(forest); + while len > 1u { + i = 0u; + while i < len - 1u {//Concat nodes 0 with 1, 2 with 3 etc. + let left = forest[i]; + let right = forest[i+1u]; + let left_len = char_len(left); + let right_len= char_len(right); + let left_height= height(left); + let right_height=height(right); + if left_len + right_len > hint_max_leaf_char_len { + if left_len <= hint_max_leaf_char_len { + left = flatten(left); + left_height = height(left); + } + if right_len <= hint_max_leaf_char_len { + right = flatten(right); + right_height = height(right); + } + } + if left_height >= hint_max_node_height { + left = of_substr_unsafer(@serialize_node(left), + 0u,byte_len(left), + left_len); + } + if right_height >= hint_max_node_height { + right = of_substr_unsafer(@serialize_node(right), + 0u,byte_len(right), + right_len); + } + forest[i/2u] = concat2(left, right); + i += 2u; + } + if i == len - 1u { + //And don't forget the last node if it is in even position + forest[i/2u] = forest[i]; + } + len = uint::div_ceil(len, 2u); + } + ret forest[0]; + } + + fn serialize_node(node: @node) -> str unsafe { + let buf = vec::init_elt_mut(0u8, byte_len(node)); + let offset = 0u;//Current position in the buffer + let it = leaf_iterator::start(node); + while true { + alt(leaf_iterator::next(it)) { + option::none. { break; } + option::some(x) { + //TODO: Replace with memcpy or something similar + let local_buf: [u8] = unsafe::reinterpret_cast(*x.content); + let i = x.byte_offset; + while i < x.byte_len { + buf[offset] = local_buf[i]; + offset += 1u; + i += 1u; + } + unsafe::leak(local_buf); + } + } + } + let str : str = unsafe::reinterpret_cast(buf); + unsafe::leak(buf);//TODO: Check if this is correct + ret str; + } + + /* + Function: flatten + + Replace a subtree by a single leaf with the same contents. + + Performance note: This function executes in linear time. + */ + fn flatten(node: @node) -> @node unsafe { + alt(*node) { + leaf(_) { ret node } + concat(x) { + ret @leaf({ + byte_offset: 0u, + byte_len: x.byte_len, + char_len: x.char_len, + content: @serialize_node(node) + }) + } + } + } + + /* + Function: bal + + Balance a node. + + Algorithm: + - if the node height is smaller than `hint_max_node_height`, do nothing + - otherwise, gather all leaves as a forest, rebuild a balanced node, + concatenating small leaves along the way + + Returns: + - `option::none` if no transformation happened + - `option::some(x)` otherwise, in which case `x` has the same contents + as `node` bot lower height and/or fragmentation. + */ + fn bal(node: @node) -> option::t<@node> { + if height(node) < hint_max_node_height { ret option::none; } + //1. Gather all leaves as a forest + let forest = [mutable]; + let it = leaf_iterator::start(node); + while true { + alt (leaf_iterator::next(it)) { + option::none. { break; } + option::some(x) { forest += [mutable @leaf(x)]; } + } + } + //2. Rebuild tree from forest + let root = @*tree_from_forest_destructive(forest); + ret option::some(root); + + } + + /* + Function: sub_bytes + + Compute the subnode of a node. + + Parameters: + node - A node + byte_offset - A byte offset in `node` + byte_len - The number of bytes to return + + Performance notes: + - this function performs no copying; + - this function executes in a time proportional to the height of `node`. + + Safety notes: + - this function fails if `byte_offset` or `byte_len` do not represent + valid positions in `node`. + */ + fn sub_bytes(node: @node, byte_offset: uint, byte_len: uint) -> @node { + let node = node; + let byte_offset = byte_offset; + while true { + if byte_offset == 0u && byte_len == node::byte_len(node) { + ret node; + } + alt(*node) { + node::leaf(x) { + let char_len = + str::char_len_range(*x.content, byte_offset, byte_len); + ret @leaf({byte_offset: byte_offset, + byte_len: byte_len, + char_len: char_len, + content: x.content}); + } + node::concat(x) { + let left_len: uint = node::byte_len(x.left); + if byte_offset <= left_len { + if byte_offset + byte_len <= left_len { + //Case 1: Everything fits in x.left, tail-call + node = x.left; + } else { + //Case 2: A (non-empty, possibly full) suffix + //of x.left and a (non-empty, possibly full) prefix + //of x.right + let left_result = + sub_bytes(x.left, byte_offset, left_len); + let right_result = + sub_bytes(x.right, 0u, left_len - byte_offset); + ret concat2(left_result, right_result); + } + } else { + //Case 3: Everything fits in x.right + byte_offset -= left_len; + node = x.right; + } + } + } + } + fail;//Note: unreachable + } + + /* + Function: sub_chars + + Compute the subnode of a node. + + Parameters: + node - A node + char_offset - A char offset in `node` + char_len - The number of chars to return + + Performance notes: + - this function performs no copying; + - this function executes in a time proportional to the height of `node`. + + Safety notes: + - this function fails if `char_offset` or `char_len` do not represent + valid positions in `node`. + */ + fn sub_chars(node: @node, char_offset: uint, char_len: uint) -> @node { + let node = node; + let char_offset = char_offset; + while true { + alt(*node) { + node::leaf(x) { + if char_offset == 0u && char_len == x.char_len { + ret node; + } + let byte_offset = + str::byte_len_range(*x.content, 0u, char_offset); + let byte_len = + str::byte_len_range(*x.content, byte_offset, char_len); + ret @leaf({byte_offset: byte_offset, + byte_len: byte_len, + char_len: char_len, + content: x.content}); + } + node::concat(x) { + if char_offset == 0u && char_len == x.char_len {ret node;} + let left_len : uint = node::char_len(x.left); + if char_offset <= left_len { + if char_offset + char_len <= left_len { + //Case 1: Everything fits in x.left, tail call + node = x.left; + } else { + //Case 2: A (non-empty, possibly full) suffix + //of x.left and a (non-empty, possibly full) prefix + //of x.right + let left_result = + sub_chars(x.left, char_offset, left_len); + let right_result = + sub_chars(x.right, 0u, left_len - char_offset); + ret concat2(left_result, right_result); + } + } else { + //Case 3: Everything fits in x.right, tail call + node = x.right; + char_offset -= left_len; + } + } + } + } + fail; + } + + fn concat2(left: @node, right: @node) -> @node { + ret @concat({left : left, + right : right, + char_len: char_len(left) + char_len(right), + byte_len: byte_len(left) + byte_len(right), + height: math::max(height(left), height(right)) + 1u + }) + } + + fn height(node: @node) -> uint { + alt(*node) { + leaf(_) { ret 0u; } + concat(x) { ret x.height; } + } + } + + fn cmp(a: @node, b: @node) -> int { + let ita = char_iterator::start(a); + let itb = char_iterator::start(b); + let result = 0; + let pos = 0u; + while result == 0 { + alt((char_iterator::next(ita), char_iterator::next(itb))) { + (option::none., option::none.) { + break; + } + (option::some(chara), option::some(charb)) { + result = char::cmp(chara, charb); + } + (option::some(_), _) { + result = 1; + } + (_, option::some(_)) { + result = -1; + } + } + pos += 1u; + } + ret result; + } + + fn loop_chars(node: @node, it: block(char) -> bool) -> bool { + ret loop_leaves(node, {|leaf| + ret str::loop_chars_sub(*leaf.content, + leaf.byte_offset, + leaf.byte_len, it) + }) + } + + /* + Function: loop_leaves + + Loop through a node, leaf by leaf + + Parameters: + + rope - A node to traverse. + it - A block to execute with each consecutive leaf of the node. + Return `true` to continue, `false` to stop. + + Returns: + + `true` If execution proceeded correctly, `false` if it was interrupted, + that is if `it` returned `false` at any point. + */ + fn loop_leaves(node: @node, it: block(leaf) -> bool) -> bool{ + let current = node; + while true { + alt(*current) { + leaf(x) { + ret it(x); + } + concat(x) { + if loop_leaves(x.left, it) { //non tail call + current = x.right; //tail call + } else { + ret false; + } + } + } + } + fail;//unreachable + } + + /* + Function: char_at + + Parameters: + pos - A position in the rope + + Returns: The character at position `pos` + + Safety notes: The function will fail if `pos` + is not a valid position in the rope. + + Performance note: This function executes in a time + proportional to the height of the rope + the (bounded) + length of the largest leaf. + */ + fn char_at(node: @node, pos: uint) -> char { + let node = node; + let pos = pos; + while true { + alt *node { + leaf(x) { + ret str::char_at(*x.content, pos); + } + concat({left, right, _}) { + let left_len = char_len(left); + node = if left_len > pos { left } + else { pos -= left_len; right }; + } + } + } + fail;//unreachable + } + + mod leaf_iterator { + type t = { + stack: [mutable @node], + mutable stackpos: int + }; + + fn empty() -> t { + let stack : [mutable @node] = [mutable]; + ret {stack: stack, mutable stackpos: -1} + } + + fn start(node: @node) -> t { + let stack = vec::init_elt_mut(node, height(node)+1u); + ret { + stack: stack, + mutable stackpos: 0 + } + } + + fn next(it: t) -> option::t<leaf> { + if it.stackpos < 0 { ret option::none; } + while true { + let current = it.stack[it.stackpos]; + it.stackpos -= 1; + alt(*current) { + concat(x) { + it.stackpos += 1; + it.stack[it.stackpos] = x.right; + it.stackpos += 1; + it.stack[it.stackpos] = x.left; + } + leaf(x) { + ret option::some(x); + } + } + } + fail;//unreachable + } + } + + mod char_iterator { + type t = { + leaf_iterator: leaf_iterator::t, + mutable leaf: option::t<leaf>, + mutable leaf_byte_pos: uint + }; + + fn start(node: @node) -> t { + ret { + leaf_iterator: leaf_iterator::start(node), + mutable leaf: option::none, + mutable leaf_byte_pos: 0u + } + } + + fn empty() -> t { + ret { + leaf_iterator: leaf_iterator::empty(), + mutable leaf: option::none, + mutable leaf_byte_pos: 0u + } + } + + fn next(it: t) -> option::t<char> { + while true { + alt(get_current_or_next_leaf(it)) { + option::none. { ret option::none; } + option::some(leaf) { + let next_char = get_next_char_in_leaf(it); + alt(next_char) { + option::none. { + cont; + } + option::some(_) { + ret next_char; + } + } + } + } + } + fail;//unreachable + } + + fn get_current_or_next_leaf(it: t) -> option::t<leaf> { + alt(it.leaf) { + option::some(_) { ret it.leaf } + option::none. { + let next = leaf_iterator::next(it.leaf_iterator); + alt(next) { + option::none. { ret option::none } + option::some(leaf) { + it.leaf = next; + it.leaf_byte_pos = 0u; + ret next; + } + } + } + } + } + + fn get_next_char_in_leaf(it: t) -> option::t<char> { + alt(it.leaf) { + option::none. { ret option::none } + option::some(leaf) { + if it.leaf_byte_pos >= leaf.byte_len { + //We are actually past the end of the leaf + it.leaf = option::none; + ret option::none + } else { + let {ch, next} = + str::char_range_at(*leaf.content, + it.leaf_byte_pos + leaf.byte_offset); + it.leaf_byte_pos = next - leaf.byte_offset; + ret option::some(ch) + } + } + } + } + } +} + diff --git a/src/libstd/run_program.rs b/src/libstd/run_program.rs new file mode 100644 index 00000000000..6f4ad2da3d6 --- /dev/null +++ b/src/libstd/run_program.rs @@ -0,0 +1,304 @@ +/* +Module: run + +Process spawning +*/ +import str::sbuf; +import ctypes::{fd_t, pid_t}; + +export program; +export run_program; +export start_program; +export program_output; +export spawn_process; +export waitpid; + +#[abi = "cdecl"] +native mod rustrt { + fn rust_run_program(argv: *sbuf, in_fd: fd_t, + out_fd: fd_t, err_fd: fd_t) -> pid_t; +} + +/* Section: Types */ + +/* +Resource: program_res + +A resource that manages the destruction of a <program> object + +program_res ensures that the destroy method is called on a +program object in order to close open file descriptors. +*/ +resource program_res(p: program) { p.destroy(); } + +/* +Obj: program + +An object representing a child process +*/ +type program = obj { + /* + Method: get_id + + Returns the process id of the program + */ + fn get_id() -> pid_t; + + /* + Method: input + + Returns an io::writer that can be used to write to stdin + */ + fn input() -> io::writer; + + /* + Method: output + + Returns an io::reader that can be used to read from stdout + */ + fn output() -> io::reader; + + /* + Method: err + + Returns an io::reader that can be used to read from stderr + */ + fn err() -> io::reader; + + /* + Method: close_input + + Closes the handle to the child processes standard input + */ + fn close_input(); + + /* + Method: finish + + Waits for the child process to terminate. Closes the handle + to stdin if necessary. + */ + fn finish() -> int; + + /* + Method: destroy + + Closes open handles + */ + fn destroy(); +}; + + +/* Section: Operations */ + +fn arg_vec(prog: str, args: [@str]) -> [sbuf] { + let argptrs = str::as_buf(prog, {|buf| [buf] }); + for arg in args { argptrs += str::as_buf(*arg, {|buf| [buf] }); } + argptrs += [ptr::null()]; + ret argptrs; +} + +/* +Function: spawn_process + +Run a program, providing stdin, stdout and stderr handles + +Parameters: + +prog - The path to an executable +args - Vector of arguments to pass to the child process +in_fd - A file descriptor for the child to use as std input +out_fd - A file descriptor for the child to use as std output +err_fd - A file descriptor for the child to use as std error + +Returns: + +The process id of the spawned process +*/ +fn spawn_process(prog: str, args: [str], in_fd: fd_t, + out_fd: fd_t, err_fd: fd_t) + -> pid_t unsafe { + // Note: we have to hold on to these vector references while we hold a + // pointer to their buffers + let prog = prog; + let args = vec::map({|arg| @arg }, args); + let argv = arg_vec(prog, args); + let pid = + rustrt::rust_run_program(vec::unsafe::to_ptr(argv), in_fd, out_fd, + err_fd); + ret pid; +} + +/* +Function: run_program + +Spawns a process and waits for it to terminate + +Parameters: + +prog - The path to an executable +args - Vector of arguments to pass to the child process + +Returns: + +The process id +*/ +fn run_program(prog: str, args: [str]) -> int { + ret waitpid(spawn_process(prog, args, 0i32, 0i32, 0i32)); +} + +/* +Function: start_program + +Spawns a process and returns a boxed <program_res> + +The returned value is a boxed resource containing a <program> object that can +be used for sending and recieving data over the standard file descriptors. +The resource will ensure that file descriptors are closed properly. + +Parameters: + +prog - The path to an executable +args - Vector of arguments to pass to the child process + +Returns: + +A boxed resource of <program> +*/ +fn start_program(prog: str, args: [str]) -> @program_res { + let pipe_input = os::pipe(); + let pipe_output = os::pipe(); + let pipe_err = os::pipe(); + let pid = + spawn_process(prog, args, pipe_input.in, pipe_output.out, + pipe_err.out); + + if pid == -1i32 { fail; } + os::libc::close(pipe_input.in); + os::libc::close(pipe_output.out); + os::libc::close(pipe_err.out); + obj new_program(pid: pid_t, + mutable in_fd: fd_t, + out_file: os::libc::FILE, + err_file: os::libc::FILE, + mutable finished: bool) { + fn get_id() -> pid_t { ret pid; } + fn input() -> io::writer { + ret io::new_writer(io::fd_buf_writer(in_fd, option::none)); + } + fn output() -> io::reader { + ret io::new_reader(io::FILE_buf_reader(out_file, option::none)); + } + fn err() -> io::reader { + ret io::new_reader(io::FILE_buf_reader(err_file, option::none)); + } + fn close_input() { + let invalid_fd = -1i32; + if in_fd != invalid_fd { + os::libc::close(in_fd); + in_fd = invalid_fd; + } + } + fn finish() -> int { + if finished { ret 0; } + finished = true; + self.close_input(); + ret waitpid(pid); + } + fn destroy() { + self.finish(); + os::libc::fclose(out_file); + os::libc::fclose(err_file); + } + } + ret @program_res(new_program(pid, pipe_input.out, + os::fd_FILE(pipe_output.in), + os::fd_FILE(pipe_err.in), false)); +} + +fn read_all(rd: io::reader) -> str { + let buf = ""; + while !rd.eof() { + let bytes = rd.read_bytes(4096u); + buf += str::unsafe_from_bytes(bytes); + } + ret buf; +} + +/* +Function: program_output + +Spawns a process, waits for it to exit, and returns the exit code, and +contents of stdout and stderr. + +Parameters: + +prog - The path to an executable +args - Vector of arguments to pass to the child process + +Returns: + +A record, {status: int, out: str, err: str} containing the exit code, +the contents of stdout and the contents of stderr. +*/ +fn program_output(prog: str, args: [str]) -> + {status: int, out: str, err: str} { + let pr = start_program(prog, args); + pr.close_input(); + let out = read_all(pr.output()); + let err = read_all(pr.err()); + ret {status: pr.finish(), out: out, err: err}; +} + +/* +Function: waitpid + +Waits for a process to exit and returns the exit code +*/ +fn waitpid(pid: pid_t) -> int { + ret waitpid_os(pid); + + #[cfg(target_os = "win32")] + fn waitpid_os(pid: pid_t) -> int { + os::waitpid(pid) as int + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] + fn waitpid_os(pid: pid_t) -> int { + #[cfg(target_os = "linux")] + fn WIFEXITED(status: i32) -> bool { + (status & 0xffi32) == 0i32 + } + + #[cfg(target_os = "macos")] + fn WIFEXITED(status: i32) -> bool { + (status & 0x7fi32) == 0i32 + } + + #[cfg(target_os = "linux")] + fn WEXITSTATUS(status: i32) -> i32 { + (status >> 8i32) & 0xffi32 + } + + #[cfg(target_os = "macos")] + fn WEXITSTATUS(status: i32) -> i32 { + status >> 8i32 + } + + let status = os::waitpid(pid); + ret if WIFEXITED(status) { + WEXITSTATUS(status) as int + } else { + 1 + }; + } +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/sha1.rs b/src/libstd/sha1.rs new file mode 100644 index 00000000000..a51cbdc65d1 --- /dev/null +++ b/src/libstd/sha1.rs @@ -0,0 +1,294 @@ +/* +Module: sha1 + +An implementation of the SHA-1 cryptographic hash. + +First create a <sha1> object using the <mk_sha1> constructor, then +feed it input using the <input> or <input_str> methods, which may be +called any number of times. + +After the entire input has been fed to the hash read the result using +the <result> or <result_str> methods. + +The <sha1> object may be reused to create multiple hashes by calling +the <reset> method. +*/ + +/* + * A SHA-1 implementation derived from Paul E. Jones's reference + * implementation, which is written for clarity, not speed. At some + * point this will want to be rewritten. + */ +export sha1; +export mk_sha1; + +/* Section: Types */ + +/* +Obj: sha1 + +The SHA-1 object +*/ +type sha1 = obj { + /* + Method: input + + Provide message input as bytes + */ + fn input([u8]); + /* + Method: input_str + + Provide message input as string + */ + fn input_str(str); + /* + Method: result + + Read the digest as a vector of 20 bytes. After calling this no further + input may be provided until reset is called. + */ + fn result() -> [u8]; + /* + Method: result_str + + Read the digest as a hex string. After calling this no further + input may be provided until reset is called. + */ + fn result_str() -> str; + /* + Method: reset + + Reset the SHA-1 state for reuse + */ + fn reset(); +}; + +/* Section: Operations */ + +// Some unexported constants +const digest_buf_len: uint = 5u; +const msg_block_len: uint = 64u; +const work_buf_len: uint = 80u; +const k0: u32 = 0x5A827999u32; +const k1: u32 = 0x6ED9EBA1u32; +const k2: u32 = 0x8F1BBCDCu32; +const k3: u32 = 0xCA62C1D6u32; + + +/* +Function: mk_sha1 + +Construct a <sha1> object +*/ +fn mk_sha1() -> sha1 { + type sha1state = + {h: [mutable u32], + mutable len_low: u32, + mutable len_high: u32, + msg_block: [mutable u8], + mutable msg_block_idx: uint, + mutable computed: bool, + work_buf: [mutable u32]}; + + fn add_input(st: sha1state, msg: [u8]) { + // FIXME: Should be typestate precondition + assert (!st.computed); + for element: u8 in msg { + st.msg_block[st.msg_block_idx] = element; + st.msg_block_idx += 1u; + st.len_low += 8u32; + if st.len_low == 0u32 { + st.len_high += 1u32; + if st.len_high == 0u32 { + // FIXME: Need better failure mode + + fail; + } + } + if st.msg_block_idx == msg_block_len { process_msg_block(st); } + } + } + fn process_msg_block(st: sha1state) { + // FIXME: Make precondition + assert (vec::len(st.h) == digest_buf_len); + assert (vec::len(st.work_buf) == work_buf_len); + let t: int; // Loop counter + let w = st.work_buf; + + // Initialize the first 16 words of the vector w + t = 0; + while t < 16 { + let tmp; + tmp = (st.msg_block[t * 4] as u32) << 24u32; + tmp = tmp | (st.msg_block[t * 4 + 1] as u32) << 16u32; + tmp = tmp | (st.msg_block[t * 4 + 2] as u32) << 8u32; + tmp = tmp | (st.msg_block[t * 4 + 3] as u32); + w[t] = tmp; + t += 1; + } + + // Initialize the rest of vector w + while t < 80 { + let val = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]; + w[t] = circular_shift(1u32, val); + t += 1; + } + let a = st.h[0]; + let b = st.h[1]; + let c = st.h[2]; + let d = st.h[3]; + let e = st.h[4]; + let temp: u32; + t = 0; + while t < 20 { + temp = circular_shift(5u32, a) + (b & c | !b & d) + e + w[t] + k0; + e = d; + d = c; + c = circular_shift(30u32, b); + b = a; + a = temp; + t += 1; + } + while t < 40 { + temp = circular_shift(5u32, a) + (b ^ c ^ d) + e + w[t] + k1; + e = d; + d = c; + c = circular_shift(30u32, b); + b = a; + a = temp; + t += 1; + } + while t < 60 { + temp = + circular_shift(5u32, a) + (b & c | b & d | c & d) + e + w[t] + + k2; + e = d; + d = c; + c = circular_shift(30u32, b); + b = a; + a = temp; + t += 1; + } + while t < 80 { + temp = circular_shift(5u32, a) + (b ^ c ^ d) + e + w[t] + k3; + e = d; + d = c; + c = circular_shift(30u32, b); + b = a; + a = temp; + t += 1; + } + st.h[0] = st.h[0] + a; + st.h[1] = st.h[1] + b; + st.h[2] = st.h[2] + c; + st.h[3] = st.h[3] + d; + st.h[4] = st.h[4] + e; + st.msg_block_idx = 0u; + } + fn circular_shift(bits: u32, word: u32) -> u32 { + ret word << bits | word >> 32u32 - bits; + } + fn mk_result(st: sha1state) -> [u8] { + if !st.computed { pad_msg(st); st.computed = true; } + let rs: [u8] = []; + for hpart: u32 in st.h { + let a = hpart >> 24u32 & 0xFFu32 as u8; + let b = hpart >> 16u32 & 0xFFu32 as u8; + let c = hpart >> 8u32 & 0xFFu32 as u8; + let d = hpart & 0xFFu32 as u8; + rs += [a, b, c, d]; + } + ret rs; + } + + /* + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 bits + * represent the length of the original message. All bits in between + * should be 0. This function will pad the message according to those + * rules by filling the msg_block vector accordingly. It will also + * call process_msg_block() appropriately. When it returns, it + * can be assumed that the message digest has been computed. + */ + fn pad_msg(st: sha1state) { + // FIXME: Should be a precondition + assert (vec::len(st.msg_block) == msg_block_len); + + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second block. + */ + if st.msg_block_idx > 55u { + st.msg_block[st.msg_block_idx] = 0x80u8; + st.msg_block_idx += 1u; + while st.msg_block_idx < msg_block_len { + st.msg_block[st.msg_block_idx] = 0u8; + st.msg_block_idx += 1u; + } + process_msg_block(st); + } else { + st.msg_block[st.msg_block_idx] = 0x80u8; + st.msg_block_idx += 1u; + } + while st.msg_block_idx < 56u { + st.msg_block[st.msg_block_idx] = 0u8; + st.msg_block_idx += 1u; + } + + // Store the message length as the last 8 octets + st.msg_block[56] = st.len_high >> 24u32 & 0xFFu32 as u8; + st.msg_block[57] = st.len_high >> 16u32 & 0xFFu32 as u8; + st.msg_block[58] = st.len_high >> 8u32 & 0xFFu32 as u8; + st.msg_block[59] = st.len_high & 0xFFu32 as u8; + st.msg_block[60] = st.len_low >> 24u32 & 0xFFu32 as u8; + st.msg_block[61] = st.len_low >> 16u32 & 0xFFu32 as u8; + st.msg_block[62] = st.len_low >> 8u32 & 0xFFu32 as u8; + st.msg_block[63] = st.len_low & 0xFFu32 as u8; + process_msg_block(st); + } + obj sha1(st: sha1state) { + fn reset() { + // FIXME: Should be typestate precondition + assert (vec::len(st.h) == digest_buf_len); + st.len_low = 0u32; + st.len_high = 0u32; + st.msg_block_idx = 0u; + st.h[0] = 0x67452301u32; + st.h[1] = 0xEFCDAB89u32; + st.h[2] = 0x98BADCFEu32; + st.h[3] = 0x10325476u32; + st.h[4] = 0xC3D2E1F0u32; + st.computed = false; + } + fn input(msg: [u8]) { add_input(st, msg); } + fn input_str(msg: str) { add_input(st, str::bytes(msg)); } + fn result() -> [u8] { ret mk_result(st); } + fn result_str() -> str { + let r = mk_result(st); + let s = ""; + for b: u8 in r { s += uint::to_str(b as uint, 16u); } + ret s; + } + } + let st = + {h: vec::init_elt_mut::<u32>(0u32, digest_buf_len), + mutable len_low: 0u32, + mutable len_high: 0u32, + msg_block: vec::init_elt_mut::<u8>(0u8, msg_block_len), + mutable msg_block_idx: 0u, + mutable computed: false, + work_buf: vec::init_elt_mut::<u32>(0u32, work_buf_len)}; + let sh = sha1(st); + sh.reset(); + ret sh; +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/smallintmap.rs b/src/libstd/smallintmap.rs new file mode 100644 index 00000000000..4702a2adab4 --- /dev/null +++ b/src/libstd/smallintmap.rs @@ -0,0 +1,81 @@ +/* +Module: smallintmap + +A simple map based on a vector for small integer keys. Space requirements +are O(highest integer key). +*/ +import option::{some, none}; + +// FIXME: Should not be @; there's a bug somewhere in rustc that requires this +// to be. +/* +Type: smallintmap +*/ +type smallintmap<T> = @{mutable v: [mutable option::t<T>]}; + +/* +Function: mk + +Create a smallintmap +*/ +fn mk<T>() -> smallintmap<T> { + let v: [mutable option::t<T>] = [mutable]; + ret @{mutable v: v}; +} + +/* +Function: insert + +Add a value to the map. If the map already contains a value for +the specified key then the original value is replaced. +*/ +fn insert<copy T>(m: smallintmap<T>, key: uint, val: T) { + vec::grow_set::<option::t<T>>(m.v, key, none::<T>, some::<T>(val)); +} + +/* +Function: find + +Get the value for the specified key. If the key does not exist +in the map then returns none. +*/ +fn find<copy T>(m: smallintmap<T>, key: uint) -> option::t<T> { + if key < vec::len::<option::t<T>>(m.v) { ret m.v[key]; } + ret none::<T>; +} + +/* +Method: get + +Get the value for the specified key + +Failure: + +If the key does not exist in the map +*/ +fn get<copy T>(m: smallintmap<T>, key: uint) -> T { + alt find(m, key) { + none. { log_err "smallintmap::get(): key not present"; fail; } + some(v) { ret v; } + } +} + +/* +Method: contains_key + +Returns true if the map contains a value for the specified key +*/ +fn contains_key<copy T>(m: smallintmap<T>, key: uint) -> bool { + ret !option::is_none(find::<T>(m, key)); +} + +// FIXME: Are these really useful? + +fn truncate<copy T>(m: smallintmap<T>, len: uint) { + m.v = vec::slice_mut::<option::t<T>>(m.v, 0u, len); +} + +fn max_key<T>(m: smallintmap<T>) -> uint { + ret vec::len::<option::t<T>>(m.v); +} + diff --git a/src/libstd/sort.rs b/src/libstd/sort.rs new file mode 100644 index 00000000000..38ab4f3cfb1 --- /dev/null +++ b/src/libstd/sort.rs @@ -0,0 +1,166 @@ +/* +Module: sort + +Sorting methods +*/ +import vec::{len, slice}; + +export merge_sort; +export quick_sort; +export quick_sort3; + +/* Type: lteq */ +type lteq<T> = block(T, T) -> bool; + +/* +Function: merge_sort + +Merge sort. Returns a new vector containing the sorted list. + +Has worst case O(n log n) performance, best case O(n), but +is not space efficient. This is a stable sort. +*/ +fn merge_sort<copy T>(le: lteq<T>, v: [const T]) -> [T] { + fn merge<copy T>(le: lteq<T>, a: [T], b: [T]) -> [T] { + let rs: [T] = []; + let a_len: uint = len::<T>(a); + let a_ix: uint = 0u; + let b_len: uint = len::<T>(b); + let b_ix: uint = 0u; + while a_ix < a_len && b_ix < b_len { + if le(a[a_ix], b[b_ix]) { + rs += [a[a_ix]]; + a_ix += 1u; + } else { rs += [b[b_ix]]; b_ix += 1u; } + } + rs += slice::<T>(a, a_ix, a_len); + rs += slice::<T>(b, b_ix, b_len); + ret rs; + } + let v_len: uint = len::<T>(v); + if v_len == 0u { ret []; } + if v_len == 1u { ret [v[0]]; } + let mid: uint = v_len / 2u; + let a: [T] = slice::<T>(v, 0u, mid); + let b: [T] = slice::<T>(v, mid, v_len); + ret merge::<T>(le, merge_sort::<T>(le, a), merge_sort::<T>(le, b)); +} + +fn part<copy T>(compare_func: lteq<T>, arr: [mutable T], left: uint, + right: uint, pivot: uint) -> uint { + let pivot_value = arr[pivot]; + arr[pivot] <-> arr[right]; + let storage_index: uint = left; + let i: uint = left; + while i < right { + if compare_func(copy arr[i], pivot_value) { + arr[i] <-> arr[storage_index]; + storage_index += 1u; + } + i += 1u; + } + arr[storage_index] <-> arr[right]; + ret storage_index; +} + +fn qsort<copy T>(compare_func: lteq<T>, arr: [mutable T], left: uint, + right: uint) { + if right > left { + let pivot = (left + right) / 2u; + let new_pivot = part::<T>(compare_func, arr, left, right, pivot); + if new_pivot != 0u { + // Need to do this check before recursing due to overflow + qsort::<T>(compare_func, arr, left, new_pivot - 1u); + } + qsort::<T>(compare_func, arr, new_pivot + 1u, right); + } +} + +/* +Function: quick_sort + +Quicksort. Sorts a mutable vector in place. + +Has worst case O(n^2) performance, average case O(n log n). +This is an unstable sort. +*/ +fn quick_sort<copy T>(compare_func: lteq<T>, arr: [mutable T]) { + if len::<T>(arr) == 0u { ret; } + qsort::<T>(compare_func, arr, 0u, len::<T>(arr) - 1u); +} + +fn qsort3<copy T>(compare_func_lt: lteq<T>, compare_func_eq: lteq<T>, + arr: [mutable T], left: int, right: int) { + if right <= left { ret; } + let v: T = arr[right]; + let i: int = left - 1; + let j: int = right; + let p: int = i; + let q: int = j; + while true { + i += 1; + while compare_func_lt(copy arr[i], v) { i += 1; } + j -= 1; + while compare_func_lt(v, copy arr[j]) { + if j == left { break; } + j -= 1; + } + if i >= j { break; } + arr[i] <-> arr[j]; + if compare_func_eq(copy arr[i], v) { + p += 1; + arr[p] <-> arr[i]; + } + if compare_func_eq(v, copy arr[j]) { + q -= 1; + arr[j] <-> arr[q]; + } + } + arr[i] <-> arr[right]; + j = i - 1; + i += 1; + let k: int = left; + while k < p { + arr[k] <-> arr[j]; + k += 1; + j -= 1; + if k == len::<T>(arr) as int { break; } + } + k = right - 1; + while k > q { + arr[i] <-> arr[k]; + k -= 1; + i += 1; + if k == 0 { break; } + } + qsort3::<T>(compare_func_lt, compare_func_eq, arr, left, j); + qsort3::<T>(compare_func_lt, compare_func_eq, arr, i, right); +} + +// FIXME: This should take lt and eq types +/* +Function: quick_sort3 + +Fancy quicksort. Sorts a mutable vector in place. + +Based on algorithm presented by Sedgewick and Bentley +<http://www.cs.princeton.edu/~rs/talks/QuicksortIsOptimal.pdf>. +According to these slides this is the algorithm of choice for +'randomly ordered keys, abstract compare' & 'small number of key values'. + +This is an unstable sort. +*/ +fn quick_sort3<copy T>(compare_func_lt: lteq<T>, compare_func_eq: lteq<T>, + arr: [mutable T]) { + if len::<T>(arr) == 0u { ret; } + qsort3::<T>(compare_func_lt, compare_func_eq, arr, 0, + (len::<T>(arr) as int) - 1); +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/std.rc b/src/libstd/std.rc new file mode 100644 index 00000000000..1f0d1b06093 --- /dev/null +++ b/src/libstd/std.rc @@ -0,0 +1,138 @@ +#[link(name = "std", + vers = "0.1", + uuid = "122bed0b-c19b-4b82-b0b7-7ae8aead7297", + url = "http://rust-lang.org/src/std")]; + +#[comment = "The Rust standard library"]; +#[license = "BSD"]; + + +export box, char, float, int, str, ptr; +export uint, u8, u32, u64, vec, bool; +export comm, fs, io, net, run, sys, task, uv; +export c_vec, ctypes, either, option, result, four, tri, util; +export bitv, deque, fun_treemap, list, map, smallintmap, sort, treemap, ufind; +export rope; +export math, math_f32, math_f64; +export ebml, dbg, getopts, json, rand, sha1, term, time, unsafe; +export extfmt, test, tempfile; +// FIXME: generic_os and os_fs shouldn't be exported +export generic_os, os, os_fs; + + +// Built-in types support modules + +mod box; +mod char; +mod float; +mod int; +mod str; +mod ptr; +mod uint; +mod u8; +mod u32; +mod u64; +mod bool; +mod vec; + + +// General io and system-services modules + +mod comm; +mod fs; +mod io; +mod net; +#[path = "run_program.rs"] +mod run; +mod sys; +mod task; +mod uv; + + +// Utility modules + +mod c_vec; +mod ctypes; +mod cmath; /* unexported */ +mod either; +mod option; +mod result; +mod four; +mod tri; +mod util; + + +// Collections + +mod bitv; +mod deque; +mod fun_treemap; +mod list; +mod map; +mod rope; +mod smallintmap; +mod sort; +mod treemap; +mod ufind; + + +// And ... other stuff + +mod ebml; +mod dbg; +mod getopts; +mod json; +mod math; +mod math_f32; +mod math_f64; +mod rand; +mod sha1; +mod tempfile; +mod term; +mod time; +mod unsafe; + +#[cfg(unicode)] +mod unicode; + + +// Compiler support modules + +mod extfmt; +mod test; + + +// Target-os module. + +// TODO: Have each os module re-export everything from genericos. +mod generic_os; + +#[cfg(target_os = "win32")] +#[path = "win32_os.rs"] +mod os; +#[cfg(target_os = "win32")] +#[path = "win32_fs.rs"] +mod os_fs; + +#[cfg(target_os = "macos")] +#[path = "macos_os.rs"] +mod os; +#[cfg(target_os = "macos")] +#[path = "posix_fs.rs"] +mod os_fs; + +#[cfg(target_os = "linux")] +#[path = "linux_os.rs"] +mod os; +#[cfg(target_os = "linux")] +#[path = "posix_fs.rs"] +mod os_fs; + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: diff --git a/src/libstd/str.rs b/src/libstd/str.rs new file mode 100644 index 00000000000..fb24c59f62f --- /dev/null +++ b/src/libstd/str.rs @@ -0,0 +1,961 @@ +/* +Module: str + +String manipulation. +*/ + +export eq, lteq, hash, is_empty, is_not_empty, is_whitespace, byte_len, + byte_len_range, index, + rindex, find, starts_with, ends_with, substr, slice, split, concat, + connect, to_upper, replace, char_slice, trim_left, trim_right, trim, + unshift_char, shift_char, pop_char, push_char, is_utf8, from_chars, + to_chars, char_len, char_len_range, char_at, bytes, is_ascii, + shift_byte, pop_byte, + unsafe_from_byte, unsafe_from_bytes, from_char, char_range_at, + str_from_cstr, sbuf, as_buf, push_byte, utf8_char_width, safe_slice, + contains, iter_chars, loop_chars, loop_chars_sub, + escape; + +#[abi = "cdecl"] +native mod rustrt { + fn rust_str_push(&s: str, ch: u8); +} + +/* +Function: eq + +Bytewise string equality +*/ +fn eq(&&a: str, &&b: str) -> bool { a == b } + +/* +Function: lteq + +Bytewise less than or equal +*/ +fn lteq(&&a: str, &&b: str) -> bool { a <= b } + +/* +Function: hash + +String hash function +*/ +fn hash(&&s: str) -> uint { + // djb hash. + // FIXME: replace with murmur. + + let u: uint = 5381u; + for c: u8 in s { u *= 33u; u += c as uint; } + ret u; +} + +// UTF-8 tags and ranges +const tag_cont_u8: u8 = 128u8; +const tag_cont: uint = 128u; +const max_one_b: uint = 128u; +const tag_two_b: uint = 192u; +const max_two_b: uint = 2048u; +const tag_three_b: uint = 224u; +const max_three_b: uint = 65536u; +const tag_four_b: uint = 240u; +const max_four_b: uint = 2097152u; +const tag_five_b: uint = 248u; +const max_five_b: uint = 67108864u; +const tag_six_b: uint = 252u; + +/* +Function: is_utf8 + +Determines if a vector uf bytes contains valid UTF-8 +*/ +fn is_utf8(v: [u8]) -> bool { + let i = 0u; + let total = vec::len::<u8>(v); + while i < total { + let chsize = utf8_char_width(v[i]); + if chsize == 0u { ret false; } + if i + chsize > total { ret false; } + i += 1u; + while chsize > 1u { + if v[i] & 192u8 != tag_cont_u8 { ret false; } + i += 1u; + chsize -= 1u; + } + } + ret true; +} + +/* +Function: is_ascii + +Determines if a string contains only ASCII characters +*/ +fn is_ascii(s: str) -> bool { + let i: uint = byte_len(s); + while i > 0u { i -= 1u; if s[i] & 128u8 != 0u8 { ret false; } } + ret true; +} + +/* +Predicate: is_empty + +Returns true if the string has length 0 +*/ +pure fn is_empty(s: str) -> bool { for c: u8 in s { ret false; } ret true; } + +/* +Predicate: is_not_empty + +Returns true if the string has length greater than 0 +*/ +pure fn is_not_empty(s: str) -> bool { !is_empty(s) } + +/* +Function: is_whitespace + +Returns true if the string contains only whitespace +*/ +fn is_whitespace(s: str) -> bool { + let i = 0u; + let len = char_len(s); + while i < len { + // FIXME: This is not how char_at works + if !char::is_whitespace(char_at(s, i)) { ret false; } + i += 1u; + } + ret true; +} + +/* +Function: byte_len + +Returns the length in bytes of a string +*/ +fn byte_len(s: str) -> uint unsafe { + let v: [u8] = unsafe::reinterpret_cast(s); + let vlen = vec::len(v); + unsafe::leak(v); + // There should always be a null terminator + assert (vlen > 0u); + ret vlen - 1u; +} + +/* +Function: byte_len_range + +As byte_len but for a substring + +Parameters: +s - A string +byte_offset - The byte offset at which to start in the string +char_len - The number of chars (not bytes!) in the range + +Returns: +The number of bytes in the substring starting at `byte_offset` and +containing `char_len` chars. + +Safety note: + +This function fails if `byte_offset` or `char_len` do not represent +valid positions in `s` +*/ +fn byte_len_range(s: str, byte_offset: uint, char_len: uint) -> uint { + let i = byte_offset; + let chars = 0u; + while chars < char_len { + let chsize = utf8_char_width(s[i]); + assert (chsize > 0u); + i += chsize; + chars += 1u; + } + ret i - byte_offset; +} + +/* +Function: bytes + +Converts a string to a vector of bytes +*/ +fn bytes(s: str) -> [u8] unsafe { + let v = unsafe::reinterpret_cast(s); + let vcopy = vec::slice(v, 0u, vec::len(v) - 1u); + unsafe::leak(v); + ret vcopy; +} + +/* +Function: unsafe_from_bytes + +Converts a vector of bytes to a string. Does not verify that the +vector contains valid UTF-8. +*/ +fn unsafe_from_bytes(v: [const u8]) -> str unsafe { + let vcopy: [u8] = v + [0u8]; + let scopy: str = unsafe::reinterpret_cast(vcopy); + unsafe::leak(vcopy); + ret scopy; +} + +/* +Function: unsafe_from_byte + +Converts a byte to a string. Does not verify that the byte is +valid UTF-8. +*/ +fn unsafe_from_byte(u: u8) -> str { unsafe_from_bytes([u]) } + +fn push_utf8_bytes(&s: str, ch: char) { + let code = ch as uint; + let bytes = + if code < max_one_b { + [code as u8] + } else if code < max_two_b { + [code >> 6u & 31u | tag_two_b as u8, code & 63u | tag_cont as u8] + } else if code < max_three_b { + [code >> 12u & 15u | tag_three_b as u8, + code >> 6u & 63u | tag_cont as u8, code & 63u | tag_cont as u8] + } else if code < max_four_b { + [code >> 18u & 7u | tag_four_b as u8, + code >> 12u & 63u | tag_cont as u8, + code >> 6u & 63u | tag_cont as u8, code & 63u | tag_cont as u8] + } else if code < max_five_b { + [code >> 24u & 3u | tag_five_b as u8, + code >> 18u & 63u | tag_cont as u8, + code >> 12u & 63u | tag_cont as u8, + code >> 6u & 63u | tag_cont as u8, code & 63u | tag_cont as u8] + } else { + [code >> 30u & 1u | tag_six_b as u8, + code >> 24u & 63u | tag_cont as u8, + code >> 18u & 63u | tag_cont as u8, + code >> 12u & 63u | tag_cont as u8, + code >> 6u & 63u | tag_cont as u8, code & 63u | tag_cont as u8] + }; + push_bytes(s, bytes); +} + +/* +Function: from_char + +Convert a char to a string +*/ +fn from_char(ch: char) -> str { + let buf = ""; + push_utf8_bytes(buf, ch); + ret buf; +} + +/* +Function: from_chars + +Convert a vector of chars to a string +*/ +fn from_chars(chs: [char]) -> str { + let buf = ""; + for ch: char in chs { push_utf8_bytes(buf, ch); } + ret buf; +} + +/* +Function: utf8_char_width + +FIXME: What does this function do? +*/ +fn utf8_char_width(b: u8) -> uint { + let byte: uint = b as uint; + if byte < 128u { ret 1u; } + if byte < 192u { + ret 0u; // Not a valid start byte + + } + if byte < 224u { ret 2u; } + if byte < 240u { ret 3u; } + if byte < 248u { ret 4u; } + if byte < 252u { ret 5u; } + ret 6u; +} + +/* +Function: char_range_at + +Pluck a character out of a string and return the index of the next character. +This function can be used to iterate over the unicode characters of a string. + +Example: + +> let s = "Clam chowder, hot sauce, pork rinds"; +> let i = 0; +> while i < len(s) { +> let {ch, next} = char_range_at(s, i); +> log ch; +> i = next; +> } + +Parameters: + +s - The string +i - The byte offset of the char to extract + +Returns: + +A record {ch: char, next: uint} containing the char value and the byte +index of the next unicode character. + +Failure: + +If `i` is greater than or equal to the length of the string. +If `i` is not the index of the beginning of a valid UTF-8 character. +*/ +fn char_range_at(s: str, i: uint) -> {ch: char, next: uint} { + let b0 = s[i]; + let w = utf8_char_width(b0); + assert (w != 0u); + if w == 1u { ret {ch: b0 as char, next: i + 1u}; } + let val = 0u; + let end = i + w; + let i = i + 1u; + while i < end { + let byte = s[i]; + assert (byte & 192u8 == tag_cont_u8); + val <<= 6u; + val += byte & 63u8 as uint; + i += 1u; + } + // Clunky way to get the right bits from the first byte. Uses two shifts, + // the first to clip off the marker bits at the left of the byte, and then + // a second (as uint) to get it to the right position. + val += (b0 << (w + 1u as u8) as uint) << (w - 1u) * 6u - w - 1u; + ret {ch: val as char, next: i}; +} + +/* +Function: char_at + +Pluck a character out of a string +*/ +fn char_at(s: str, i: uint) -> char { ret char_range_at(s, i).ch; } + +/* +Function: iter_chars + +Iterate over the characters in a string +*/ + +fn iter_chars(s: str, it: block(char)) { + let pos = 0u, len = byte_len(s); + while (pos < len) { + let {ch, next} = char_range_at(s, pos); + pos = next; + it(ch); + } +} + +/* +Function: loop_chars + +Loop through a string, char by char + +Parameters: +s - A string to traverse. It may be empty. +it - A block to execute with each consecutive character of `s`. +Return `true` to continue, `false` to stop. + +Returns: + +`true` If execution proceeded correctly, `false` if it was interrupted, +that is if `it` returned `false` at any point. + */ +fn loop_chars(s: str, it: block(char) -> bool) -> bool{ + ret loop_chars_sub(s, 0u, byte_len(s), it); +} + +/* +Function: loop_chars_sub + +Loop through a substring, char by char + +Parameters: +s - A string to traverse. It may be empty. +byte_offset - The byte offset at which to start in the string. +byte_len - The number of bytes to traverse in the string +it - A block to execute with each consecutive character of `s`. +Return `true` to continue, `false` to stop. + +Returns: + +`true` If execution proceeded correctly, `false` if it was interrupted, +that is if `it` returned `false` at any point. + +Safety note: +- This function does not check whether the substring is valid. +- This function fails if `byte_offset` or `byte_len` do not + represent valid positions inside `s` + */ +fn loop_chars_sub(s: str, byte_offset: uint, byte_len: uint, + it: block(char) -> bool) -> bool { + let i = byte_offset; + let result = true; + while i < byte_len { + let {ch, next} = char_range_at(s, i); + if !it(ch) {result = false; break;} + i = next; + } + ret result; +} + + +/* +Function: char_len + +Count the number of unicode characters in a string +*/ +fn char_len(s: str) -> uint { + ret char_len_range(s, 0u, byte_len(s)); +} + +/* +Function: char_len_range + +As char_len but for a slice of a string + +Parameters: + s - A valid string + byte_start - The position inside `s` where to start counting in bytes. + byte_len - The number of bytes of `s` to take into account. + +Returns: + The number of Unicode characters in `s` in +segment [byte_start, byte_start+len( . + +Safety note: +- This function does not check whether the substring is valid. +- This function fails if `byte_offset` or `byte_len` do not + represent valid positions inside `s` +*/ +fn char_len_range(s: str, byte_start: uint, byte_len: uint) -> uint { + let i = byte_start; + let len = 0u; + while i < byte_len { + let chsize = utf8_char_width(s[i]); + assert (chsize > 0u); + len += 1u; + i += chsize; + } + assert (i == byte_len); + ret len; +} + +/* +Function: to_chars + +Convert a string to a vector of characters +*/ +fn to_chars(s: str) -> [char] { + let buf: [char] = []; + let i = 0u; + let len = byte_len(s); + while i < len { + let cur = char_range_at(s, i); + buf += [cur.ch]; + i = cur.next; + } + ret buf; +} + +/* +Function: push_char + +Append a character to a string +*/ +fn push_char(&s: str, ch: char) { s += from_char(ch); } + +/* +Function: pop_char + +Remove the final character from a string and return it. + +Failure: + +If the string does not contain any characters. +*/ +fn pop_char(&s: str) -> char { + let end = byte_len(s); + while end > 0u && s[end - 1u] & 192u8 == tag_cont_u8 { end -= 1u; } + assert (end > 0u); + let ch = char_at(s, end - 1u); + s = substr(s, 0u, end - 1u); + ret ch; +} + +/* +Function: shift_char + +Remove the first character from a string and return it. + +Failure: + +If the string does not contain any characters. +*/ +fn shift_char(&s: str) -> char { + let r = char_range_at(s, 0u); + s = substr(s, r.next, byte_len(s) - r.next); + ret r.ch; +} + +/* +Function: unshift_char + +Prepend a char to a string +*/ +fn unshift_char(&s: str, ch: char) { s = from_char(ch) + s; } + +/* +Function: index + +Returns the index of the first matching byte. Returns -1 if +no match is found. +*/ +fn index(s: str, c: u8) -> int { + let i: int = 0; + for k: u8 in s { if k == c { ret i; } i += 1; } + ret -1; +} + +/* +Function: rindex + +Returns the index of the last matching byte. Returns -1 +if no match is found. +*/ +fn rindex(s: str, c: u8) -> int { + let n: int = byte_len(s) as int; + while n >= 0 { if s[n] == c { ret n; } n -= 1; } + ret n; +} + +/* +Function: find + +Finds the index of the first matching substring. +Returns -1 if `haystack` does not contain `needle`. + +Parameters: + +haystack - The string to look in +needle - The string to look for + +Returns: + +The index of the first occurance of `needle`, or -1 if not found. +*/ +fn find(haystack: str, needle: str) -> int { + let haystack_len: int = byte_len(haystack) as int; + let needle_len: int = byte_len(needle) as int; + if needle_len == 0 { ret 0; } + fn match_at(haystack: str, needle: str, i: int) -> bool { + let j: int = i; + for c: u8 in needle { if haystack[j] != c { ret false; } j += 1; } + ret true; + } + let i: int = 0; + while i <= haystack_len - needle_len { + if match_at(haystack, needle, i) { ret i; } + i += 1; + } + ret -1; +} + +/* +Function: contains + +Returns true if one string contains another + +Parameters: + +haystack - The string to look in +needle - The string to look for +*/ +fn contains(haystack: str, needle: str) -> bool { + 0 <= find(haystack, needle) +} + +/* +Function: starts_with + +Returns true if one string starts with another + +Parameters: + +haystack - The string to look in +needle - The string to look for +*/ +fn starts_with(haystack: str, needle: str) -> bool { + let haystack_len: uint = byte_len(haystack); + let needle_len: uint = byte_len(needle); + if needle_len == 0u { ret true; } + if needle_len > haystack_len { ret false; } + ret eq(substr(haystack, 0u, needle_len), needle); +} + +/* +Function: ends_with + +Returns true if one string ends with another + +haystack - The string to look in +needle - The string to look for +*/ +fn ends_with(haystack: str, needle: str) -> bool { + let haystack_len: uint = byte_len(haystack); + let needle_len: uint = byte_len(needle); + ret if needle_len == 0u { + true + } else if needle_len > haystack_len { + false + } else { + eq(substr(haystack, haystack_len - needle_len, needle_len), + needle) + }; +} + +/* +Function: substr + +Take a substring of another. Returns a string containing `len` bytes +starting at byte offset `begin`. + +This function is not unicode-safe. + +Failure: + +If `begin` + `len` is is greater than the byte length of the string +*/ +fn substr(s: str, begin: uint, len: uint) -> str { + ret slice(s, begin, begin + len); +} + +/* +Function: slice + +Takes a bytewise slice from a string. Returns the substring from +[`begin`..`end`). + +This function is not unicode-safe. + +Failure: + +- If begin is greater than end. +- If end is greater than the length of the string. +*/ +fn slice(s: str, begin: uint, end: uint) -> str unsafe { + // FIXME: Typestate precondition + assert (begin <= end); + assert (end <= byte_len(s)); + + let v: [u8] = unsafe::reinterpret_cast(s); + let v2 = vec::slice(v, begin, end); + unsafe::leak(v); + v2 += [0u8]; + let s2: str = unsafe::reinterpret_cast(v2); + unsafe::leak(v2); + ret s2; +} + +/* +Function: safe_slice +*/ +fn safe_slice(s: str, begin: uint, end: uint) : uint::le(begin, end) -> str { + // would need some magic to make this a precondition + assert (end <= byte_len(s)); + ret slice(s, begin, end); +} + +/* +Function: shift_byte + +Removes the first byte from a string and returns it. + +This function is not unicode-safe. +*/ +fn shift_byte(&s: str) -> u8 { + let len = byte_len(s); + assert (len > 0u); + let b = s[0]; + s = substr(s, 1u, len - 1u); + ret b; +} + +/* +Function: pop_byte + +Removes the last byte from a string and returns it. + +This function is not unicode-safe. +*/ +fn pop_byte(&s: str) -> u8 { + let len = byte_len(s); + assert (len > 0u); + let b = s[len - 1u]; + s = substr(s, 0u, len - 1u); + ret b; +} + +/* +Function: push_byte + +Appends a byte to a string. + +This function is not unicode-safe. +*/ +fn push_byte(&s: str, b: u8) { rustrt::rust_str_push(s, b); } + +/* +Function: push_bytes + +Appends a vector of bytes to a string. + +This function is not unicode-safe. +*/ +fn push_bytes(&s: str, bytes: [u8]) { + for byte in bytes { rustrt::rust_str_push(s, byte); } +} + +/* +Function: split + +Split a string at each occurance of a given separator + +Returns: + +A vector containing all the strings between each occurance of the separator +*/ +fn split(s: str, sep: u8) -> [str] { + let v: [str] = []; + let accum: str = ""; + let ends_with_sep: bool = false; + for c: u8 in s { + if c == sep { + v += [accum]; + accum = ""; + ends_with_sep = true; + } else { accum += unsafe_from_byte(c); ends_with_sep = false; } + } + if byte_len(accum) != 0u || ends_with_sep { v += [accum]; } + ret v; +} + +/* +Function: concat + +Concatenate a vector of strings +*/ +fn concat(v: [str]) -> str { + let s: str = ""; + for ss: str in v { s += ss; } + ret s; +} + +/* +Function: connect + +Concatenate a vector of strings, placing a given separator between each +*/ +fn connect(v: [str], sep: str) -> str { + let s: str = ""; + let first: bool = true; + for ss: str in v { + if first { first = false; } else { s += sep; } + s += ss; + } + ret s; +} + +// FIXME: This only handles ASCII +/* +Function: to_upper + +Convert a string to uppercase +*/ +fn to_upper(s: str) -> str { + let outstr = ""; + let ascii_a = 'a' as u8; + let ascii_z = 'z' as u8; + let diff = 32u8; + for byte: u8 in s { + let next; + if ascii_a <= byte && byte <= ascii_z { + next = byte - diff; + } else { next = byte; } + push_byte(outstr, next); + } + ret outstr; +} + +// FIXME: This is super-inefficient +/* +Function: replace + +Replace all occurances of one string with another + +Parameters: + +s - The string containing substrings to replace +from - The string to replace +to - The replacement string + +Returns: + +The original string with all occurances of `from` replaced with `to` +*/ +fn replace(s: str, from: str, to: str) : is_not_empty(from) -> str { + // FIXME (694): Shouldn't have to check this + check (is_not_empty(from)); + if byte_len(s) == 0u { + ret ""; + } else if starts_with(s, from) { + ret to + replace(slice(s, byte_len(from), byte_len(s)), from, to); + } else { + ret unsafe_from_byte(s[0]) + + replace(slice(s, 1u, byte_len(s)), from, to); + } +} + +// FIXME: Also not efficient +/* +Function: char_slice + +Unicode-safe slice. Returns a slice of the given string containing +the characters in the range [`begin`..`end`). `begin` and `end` are +character indexes, not byte indexes. + +Failure: + +- If begin is greater than end +- If end is greater than the character length of the string +*/ +fn char_slice(s: str, begin: uint, end: uint) -> str { + from_chars(vec::slice(to_chars(s), begin, end)) +} + +/* +Function: trim_left + +Returns a string with leading whitespace removed. +*/ +fn trim_left(s: str) -> str { + fn count_whities(s: [char]) -> uint { + let i = 0u; + while i < vec::len(s) { + if !char::is_whitespace(s[i]) { break; } + i += 1u; + } + ret i; + } + let chars = to_chars(s); + let whities = count_whities(chars); + ret from_chars(vec::slice(chars, whities, vec::len(chars))); +} + +/* +Function: trim_right + +Returns a string with trailing whitespace removed. +*/ +fn trim_right(s: str) -> str { + fn count_whities(s: [char]) -> uint { + let i = vec::len(s); + while 0u < i { + if !char::is_whitespace(s[i - 1u]) { break; } + i -= 1u; + } + ret i; + } + let chars = to_chars(s); + let whities = count_whities(chars); + ret from_chars(vec::slice(chars, 0u, whities)); +} + +/* +Function: trim + +Returns a string with leading and trailing whitespace removed +*/ +fn trim(s: str) -> str { trim_left(trim_right(s)) } + +/* +Type: sbuf + +An unsafe buffer of bytes. Corresponds to a C char pointer. +*/ +type sbuf = *u8; + +// NB: This is intentionally unexported because it's easy to misuse (there's +// no guarantee that the string is rooted). Instead, use as_buf below. +unsafe fn buf(s: str) -> sbuf { + let saddr = ptr::addr_of(s); + let vaddr: *[u8] = unsafe::reinterpret_cast(saddr); + let buf = vec::to_ptr(*vaddr); + ret buf; +} + +/* +Function: as_buf + +Work with the byte buffer of a string. Allows for unsafe manipulation +of strings, which is useful for native interop. + +Example: + +> let s = str::as_buf("PATH", { |path_buf| libc::getenv(path_buf) }); + +*/ +fn as_buf<T>(s: str, f: block(sbuf) -> T) -> T unsafe { + let buf = buf(s); f(buf) +} + +/* +Function: str_from_cstr + +Create a Rust string from a null-terminated C string +*/ +unsafe fn str_from_cstr(cstr: sbuf) -> str { + let res = ""; + let start = cstr; + let curr = start; + let i = 0u; + while *curr != 0u8 { + push_byte(res, *curr); + i += 1u; + curr = ptr::offset(start, i); + } + ret res; +} + +/* +Function: escape_char + +Escapes a single character. +*/ +fn escape_char(c: char) -> str { + alt c { + '"' { "\\\"" } + '\\' { "\\\\" } +// TODO: uncomment these when https://github.com/graydon/rust/issues/1170 is +// fixed. +// '\n' { "\\n" } +// '\t' { "\\t" } +// '\r' { "\\r" } + '\x00' to '\x1f' { #fmt["\\x%02x", c as uint] } + v { from_char(c) } + } +} + +/* +Function: escape + +Escapes special characters inside the string, making it safe for transfer. +*/ +fn escape(s: str) -> str { + let r = ""; + loop_chars(s, { |c| r += escape_char(c); true }); + r +} diff --git a/src/libstd/sys.rs b/src/libstd/sys.rs new file mode 100644 index 00000000000..3b4a3b8c643 --- /dev/null +++ b/src/libstd/sys.rs @@ -0,0 +1,96 @@ +/* +Module: sys + +Misc low level stuff +*/ +tag type_desc = { + first_param: **ctypes::c_int, + size: ctypes::size_t, + align: ctypes::size_t + // Remaining fields not listed +}; + +#[abi = "cdecl"] +native mod rustrt { + // Explicitly re-export native stuff we want to be made + // available outside this crate. Otherwise it's + // visible-in-crate, but not re-exported. + fn last_os_error() -> str; + fn refcount<T>(t: @T) -> uint; + fn do_gc(); + fn unsupervise(); +} + +#[abi = "rust-intrinsic"] +native mod rusti { + fn get_type_desc<T>() -> *type_desc; +} + +/* +Function: get_type_desc + +Returns a pointer to a type descriptor. Useful for calling certain +function in the Rust runtime or otherwise performing dark magick. +*/ +fn get_type_desc<T>() -> *type_desc { + ret rusti::get_type_desc::<T>(); +} + +/* +Function: last_os_error + +Get a string representing the platform-dependent last error +*/ +fn last_os_error() -> str { + ret rustrt::last_os_error(); +} + +/* +Function: size_of + +Returns the size of a type +*/ +fn size_of<T>() -> uint unsafe { + ret (*get_type_desc::<T>()).size; +} + +/* +Function: align_of + +Returns the alignment of a type +*/ +fn align_of<T>() -> uint unsafe { + ret (*get_type_desc::<T>()).align; +} + +/* +Function: refcount + +Returns the refcount of a shared box +*/ +fn refcount<T>(t: @T) -> uint { + ret rustrt::refcount::<T>(t); +} + +/* +Function: do_gc + +Force a garbage collection +*/ +fn do_gc() -> () { + ret rustrt::do_gc(); +} + +// FIXME: There's a wrapper for this in the task module and this really +// just belongs there +fn unsupervise() -> () { + ret rustrt::unsupervise(); +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/task.rs b/src/libstd/task.rs new file mode 100644 index 00000000000..a8765407f3a --- /dev/null +++ b/src/libstd/task.rs @@ -0,0 +1,350 @@ +/* +Module: task + +Task management. + +An executing Rust program consists of a tree of tasks, each with their own +stack, and sole ownership of their allocated heap data. Tasks communicate +with each other using ports and channels. + +When a task fails, that failure will propagate to its parent (the task +that spawned it) and the parent will fail as well. The reverse is not +true: when a parent task fails its children will continue executing. When +the root (main) task fails, all tasks fail, and then so does the entire +process. + +A task may remove itself from this failure propagation mechanism by +calling the <unsupervise> function, after which failure will only +result in the termination of that task. + +Tasks may execute in parallel and are scheduled automatically by the runtime. + +Example: + +> spawn("Hello, World", fn (&&msg: str) { +> log msg; +> }); + +*/ +import cast = unsafe::reinterpret_cast; +import comm; +import option::{some, none}; +import option = option::t; +import ptr; + +export task; +export joinable_task; +export sleep; +export yield; +export task_notification; +export join; +export unsupervise; +export pin; +export unpin; +export set_min_stack; +export task_result; +export tr_success; +export tr_failure; +export get_task; +export spawn; +export spawn_notify; +export spawn_joinable; + +#[abi = "rust-intrinsic"] +native mod rusti { + // these must run on the Rust stack so that they can swap stacks etc: + fn task_sleep(task: *rust_task, time_in_us: uint, &killed: bool); +} + +#[link_name = "rustrt"] +#[abi = "cdecl"] +native mod rustrt { + // these can run on the C stack: + fn pin_task(); + fn unpin_task(); + fn get_task_id() -> task_id; + fn rust_get_task() -> *rust_task; + + fn set_min_stack(stack_size: uint); + + fn new_task() -> task_id; + fn drop_task(task_id: *rust_task); + fn get_task_pointer(id: task_id) -> *rust_task; + + fn migrate_alloc(alloc: *u8, target: task_id); + + fn start_task(id: task, closure: *u8); + +} + +/* Section: Types */ + +type rust_task = + {id: task, + mutable notify_enabled: int, + mutable notify_chan: comm::chan<task_notification>, + mutable stack_ptr: *u8}; + +resource rust_task_ptr(task: *rust_task) { rustrt::drop_task(task); } + +type task_id = int; + +/* +Type: task + +A handle to a task +*/ +type task = task_id; + +/* +Type: joinable_task + +A task that sends notification upon termination +*/ +type joinable_task = (task, comm::port<task_notification>); + +/* +Tag: task_result + +Indicates the manner in which a task exited +*/ +tag task_result { + /* Variant: tr_success */ + tr_success; + /* Variant: tr_failure */ + tr_failure; +} + +/* +Tag: task_notification + +Message sent upon task exit to indicate normal or abnormal termination +*/ +tag task_notification { + /* Variant: exit */ + exit(task, task_result); +} + +/* Section: Operations */ + +/* +Type: get_task + +Retreives a handle to the currently executing task +*/ +fn get_task() -> task { rustrt::get_task_id() } + +/* +Function: sleep + +Hints the scheduler to yield this task for a specified ammount of time. + +Parameters: + +time_in_us - maximum number of microseconds to yield control for +*/ +fn sleep(time_in_us: uint) { + let task = rustrt::rust_get_task(); + let killed = false; + log #fmt("yielding for %u us", time_in_us); + rusti::task_sleep(task, time_in_us, killed); + if killed { + fail "killed"; + } +} + +/* +Function: yield + +Yield control to the task scheduler + +The scheduler may schedule another task to execute. +*/ +fn yield() { sleep(1u) } + +/* +Function: join + +Wait for a child task to exit + +The child task must have been spawned with <spawn_joinable>, which +produces a notification port that the child uses to communicate its +exit status. + +Returns: + +A task_result indicating whether the task terminated normally or failed +*/ +fn join(task_port: joinable_task) -> task_result { + let (id, port) = task_port; + alt comm::recv::<task_notification>(port) { + exit(_id, res) { + if _id == id { + ret res + } else { fail #fmt["join received id %d, expected %d", _id, id] } + } + } +} + +/* +Function: unsupervise + +Detaches this task from its parent in the task tree + +An unsupervised task will not propagate its failure up the task tree +*/ +fn unsupervise() { ret sys::unsupervise(); } + +/* +Function: pin + +Pins the current task and future child tasks to a single scheduler thread +*/ +fn pin() { rustrt::pin_task(); } + +/* +Function: unpin + +Unpin the current task and future child tasks +*/ +fn unpin() { rustrt::unpin_task(); } + +/* +Function: set_min_stack + +Set the minimum stack size (in bytes) for tasks spawned in the future. + +This function has global effect and should probably not be used. +*/ +fn set_min_stack(stack_size: uint) { rustrt::set_min_stack(stack_size); } + +/* +Function: spawn + +Creates and executes a new child task + +Sets up a new task with its own call stack and schedules it to be executed. +Upon execution the new task will call function `f` with the provided +argument `data`. + +Function `f` is a bare function, meaning it may not close over any data, as do +shared functions (fn@) and lambda blocks. `data` must be a uniquely owned +type; it is moved into the new task and thus can no longer be accessed +locally. + +Parameters: + +data - A unique-type value to pass to the new task +f - A function to execute in the new task + +Returns: + +A handle to the new task +*/ +fn spawn<send T>(-data: T, f: fn(T)) -> task { + spawn_inner(data, f, none) +} + +/* +Function: spawn_notify + +Create and execute a new child task, requesting notification upon its +termination + +Immediately before termination, either on success or failure, the spawned +task will send a <task_notification> message on the provided channel. +*/ +fn spawn_notify<send T>(-data: T, f: fn(T), + notify: comm::chan<task_notification>) -> task { + spawn_inner(data, f, some(notify)) +} + +/* +Function: spawn_joinable + +Create and execute a task which can later be joined with the <join> function + +This is a convenience wrapper around spawn_notify which, when paired +with <join> can be easily used to spawn a task then wait for it to +complete. +*/ +fn spawn_joinable<send T>(-data: T, f: fn(T)) -> joinable_task { + let p = comm::port::<task_notification>(); + let id = spawn_notify(data, f, comm::chan::<task_notification>(p)); + ret (id, p); +} + +// FIXME: To transition from the unsafe spawn that spawns a shared closure to +// the safe spawn that spawns a bare function we're going to write +// barefunc-spawn on top of unsafe-spawn. Sadly, bind does not work reliably +// enough to suite our needs (#1034, probably others yet to be discovered), so +// we're going to copy the bootstrap data into a unique pointer, cast it to an +// unsafe pointer then wrap up the bare function and the unsafe pointer in a +// shared closure to spawn. +// +// After the transition this should all be rewritten. + +fn spawn_inner<send T>(-data: T, f: fn(T), + notify: option<comm::chan<task_notification>>) + -> task unsafe { + + fn wrapper<send T>(-data: *u8, f: fn(T)) unsafe { + let data: ~T = unsafe::reinterpret_cast(data); + f(*data); + } + + let data = ~data; + let dataptr: *u8 = unsafe::reinterpret_cast(data); + unsafe::leak(data); + let wrapped = bind wrapper(dataptr, f); + ret unsafe_spawn_inner(wrapped, notify); +} + +// FIXME: This is the old spawn function that spawns a shared closure. +// It is a hack and needs to be rewritten. +fn unsafe_spawn_inner(-thunk: fn@(), + notify: option<comm::chan<task_notification>>) -> + task unsafe { + let id = rustrt::new_task(); + + let raw_thunk: {code: uint, env: uint} = cast(thunk); + + // set up the task pointer + let task_ptr <- rust_task_ptr(rustrt::get_task_pointer(id)); + + assert (ptr::null() != (**task_ptr).stack_ptr); + + // copy the thunk from our stack to the new stack + let sp: uint = cast((**task_ptr).stack_ptr); + let ptrsize = sys::size_of::<*u8>(); + let thunkfn: *mutable uint = cast(sp - ptrsize * 2u); + let thunkenv: *mutable uint = cast(sp - ptrsize); + *thunkfn = cast(raw_thunk.code);; + *thunkenv = cast(raw_thunk.env);; + // align the stack to 16 bytes + (**task_ptr).stack_ptr = cast(sp - ptrsize * 4u); + + // set up notifications if they are enabled. + alt notify { + some(c) { + (**task_ptr).notify_enabled = 1; + (**task_ptr).notify_chan = c; + } + none { } + } + + // give the thunk environment's allocation to the new task + rustrt::migrate_alloc(cast(raw_thunk.env), id); + rustrt::start_task(id, cast(thunkfn)); + // don't cleanup the thunk in this task + unsafe::leak(thunk); + ret id; +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/tempfile.rs b/src/libstd/tempfile.rs new file mode 100644 index 00000000000..5f504ba2349 --- /dev/null +++ b/src/libstd/tempfile.rs @@ -0,0 +1,23 @@ +/* +Module: tempfile + +Temporary files and directories +*/ + +import fs; +import option; +import option::{none, some}; +import rand; + +fn mkdtemp(prefix: str, suffix: str) -> option::t<str> { + let r = rand::mk_rng(); + let i = 0u; + while (i < 1000u) { + let s = prefix + r.gen_str(16u) + suffix; + if fs::make_dir(s, 0x1c0i32) { // FIXME: u+rwx + ret some(s); + } + i += 1u; + } + ret none; +} diff --git a/src/libstd/term.rs b/src/libstd/term.rs new file mode 100644 index 00000000000..75c35aabd54 --- /dev/null +++ b/src/libstd/term.rs @@ -0,0 +1,107 @@ +/* +Module: term + +Simple ANSI color library +*/ + +// TODO: Windows support. + +/* Const: color_black */ +const color_black: u8 = 0u8; +/* Const: color_red */ +const color_red: u8 = 1u8; +/* Const: color_green */ +const color_green: u8 = 2u8; +/* Const: color_yellow */ +const color_yellow: u8 = 3u8; +/* Const: color_blue */ +const color_blue: u8 = 4u8; +/* Const: color_magenta */ +const color_magenta: u8 = 5u8; +/* Const: color_cyan */ +const color_cyan: u8 = 6u8; +/* Const: color_light_gray */ +const color_light_gray: u8 = 7u8; +/* Const: color_light_grey */ +const color_light_grey: u8 = 7u8; +/* Const: color_dark_gray */ +const color_dark_gray: u8 = 8u8; +/* Const: color_dark_grey */ +const color_dark_grey: u8 = 8u8; +/* Const: color_bright_red */ +const color_bright_red: u8 = 9u8; +/* Const: color_bright_green */ +const color_bright_green: u8 = 10u8; +/* Const: color_bright_yellow */ +const color_bright_yellow: u8 = 11u8; +/* Const: color_bright_blue */ +const color_bright_blue: u8 = 12u8; +/* Const: color_bright_magenta */ +const color_bright_magenta: u8 = 13u8; +/* Const: color_bright_cyan */ +const color_bright_cyan: u8 = 14u8; +/* Const: color_bright_white */ +const color_bright_white: u8 = 15u8; + +fn esc(writer: io::buf_writer) { writer.write([0x1bu8, '[' as u8]); } + +/* +Function: reset + +Reset the foreground and background colors to default +*/ +fn reset(writer: io::buf_writer) { + esc(writer); + writer.write(['0' as u8, 'm' as u8]); +} + +/* +Function: color_supported + +Returns true if the terminal supports color +*/ +fn color_supported() -> bool { + let supported_terms = ["xterm-color", "xterm", "screen-bce"]; + ret alt generic_os::getenv("TERM") { + option::some(env) { + for term: str in supported_terms { + if str::eq(term, env) { ret true; } + } + false + } + option::none. { false } + }; +} + +fn set_color(writer: io::buf_writer, first_char: u8, color: u8) { + assert (color < 16u8); + esc(writer); + let color = color; + if color >= 8u8 { writer.write(['1' as u8, ';' as u8]); color -= 8u8; } + writer.write([first_char, ('0' as u8) + color, 'm' as u8]); +} + +/* +Function: fg + +Set the foreground color +*/ +fn fg(writer: io::buf_writer, color: u8) { + ret set_color(writer, '3' as u8, color); +} + +/* +Function: fg + +Set the background color +*/ +fn bg(writer: io::buf_writer, color: u8) { + ret set_color(writer, '4' as u8, color); +} + +// Local Variables: +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/test.rs b/src/libstd/test.rs new file mode 100644 index 00000000000..d827d775831 --- /dev/null +++ b/src/libstd/test.rs @@ -0,0 +1,353 @@ +// Support code for rustc's built in test runner generator. Currently, +// none of this is meant for users. It is intended to support the +// simplest interface possible for representing and running tests +// while providing a base that other test frameworks may build off of. + +import task::task; + +export test_name; +export test_fn; +export default_test_fn; +export test_desc; +export test_main; +export test_result; +export test_opts; +export tr_ok; +export tr_failed; +export tr_ignored; +export run_tests_console; +export run_tests_console_; +export run_test; +export filter_tests; +export parse_opts; +export test_to_task; +export default_test_to_task; +export configure_test_task; +export joinable; + +#[abi = "cdecl"] +native mod rustrt { + fn sched_threads() -> uint; +} + + +// The name of a test. By convention this follows the rules for rust +// paths; i.e. it should be a series of identifiers seperated by double +// colons. This way if some test runner wants to arrange the tests +// hierarchically it may. +type test_name = str; + +// A function that runs a test. If the function returns successfully, +// the test succeeds; if the function fails then the test fails. We +// may need to come up with a more clever definition of test in order +// to support isolation of tests into tasks. +type test_fn<T> = T; + +type default_test_fn = test_fn<fn()>; + +// The definition of a single test. A test runner will run a list of +// these. +type test_desc<T> = { + name: test_name, + fn: test_fn<T>, + ignore: bool, + should_fail: bool +}; + +// The default console test runner. It accepts the command line +// arguments and a vector of test_descs (generated at compile time). +fn test_main(args: [str], tests: [test_desc<default_test_fn>]) { + check (vec::is_not_empty(args)); + let opts = + alt parse_opts(args) { + either::left(o) { o } + either::right(m) { fail m } + }; + if !run_tests_console(opts, tests) { fail "Some tests failed"; } +} + +type test_opts = {filter: option::t<str>, run_ignored: bool}; + +type opt_res = either::t<test_opts, str>; + +// Parses command line arguments into test options +fn parse_opts(args: [str]) : vec::is_not_empty(args) -> opt_res { + + let args_ = vec::tail(args); + let opts = [getopts::optflag("ignored")]; + let match = + alt getopts::getopts(args_, opts) { + getopts::success(m) { m } + getopts::failure(f) { ret either::right(getopts::fail_str(f)) } + }; + + let filter = + if vec::len(match.free) > 0u { + option::some(match.free[0]) + } else { option::none }; + + let run_ignored = getopts::opt_present(match, "ignored"); + + let test_opts = {filter: filter, run_ignored: run_ignored}; + + ret either::left(test_opts); +} + +tag test_result { tr_ok; tr_failed; tr_ignored; } + +type joinable = (task, comm::port<task::task_notification>); + +// To get isolation and concurrency tests have to be run in their own tasks. +// In cases where test functions are closures it is not ok to just dump them +// into a task and run them, so this transformation gives the caller a chance +// to create the test task. +type test_to_task<T> = fn@(test_fn<T>) -> joinable; + +// A simple console test runner +fn run_tests_console(opts: test_opts, + tests: [test_desc<default_test_fn>]) -> bool { + run_tests_console_(opts, tests, default_test_to_task) +} + +fn run_tests_console_<copy T>(opts: test_opts, tests: [test_desc<T>], + to_task: test_to_task<T>) -> bool { + + type test_state = + @{out: io::writer, + use_color: bool, + mutable total: uint, + mutable passed: uint, + mutable failed: uint, + mutable ignored: uint, + mutable failures: [test_desc<T>]}; + + fn callback<copy T>(event: testevent<T>, st: test_state) { + alt event { + te_filtered(filtered_tests) { + st.total = vec::len(filtered_tests); + st.out.write_line(#fmt["\nrunning %u tests", st.total]); + } + te_wait(test) { st.out.write_str(#fmt["test %s ... ", test.name]); } + te_result(test, result) { + alt result { + tr_ok. { + st.passed += 1u; + write_ok(st.out, st.use_color); + st.out.write_line(""); + } + tr_failed. { + st.failed += 1u; + write_failed(st.out, st.use_color); + st.out.write_line(""); + st.failures += [test]; + } + tr_ignored. { + st.ignored += 1u; + write_ignored(st.out, st.use_color); + st.out.write_line(""); + } + } + } + } + } + + let st = + @{out: io::stdout(), + use_color: use_color(), + mutable total: 0u, + mutable passed: 0u, + mutable failed: 0u, + mutable ignored: 0u, + mutable failures: []}; + + run_tests(opts, tests, to_task, bind callback(_, st)); + + assert (st.passed + st.failed + st.ignored == st.total); + let success = st.failed == 0u; + + if !success { + st.out.write_line("\nfailures:"); + for test: test_desc<T> in st.failures { + let testname = test.name; // Satisfy alias analysis + st.out.write_line(#fmt[" %s", testname]); + } + } + + st.out.write_str(#fmt["\nresult: "]); + if success { + // There's no parallelism at this point so it's safe to use color + write_ok(st.out, true); + } else { write_failed(st.out, true); } + st.out.write_str(#fmt[". %u passed; %u failed; %u ignored\n\n", st.passed, + st.failed, st.ignored]); + + ret success; + + fn write_ok(out: io::writer, use_color: bool) { + write_pretty(out, "ok", term::color_green, use_color); + } + + fn write_failed(out: io::writer, use_color: bool) { + write_pretty(out, "FAILED", term::color_red, use_color); + } + + fn write_ignored(out: io::writer, use_color: bool) { + write_pretty(out, "ignored", term::color_yellow, use_color); + } + + fn write_pretty(out: io::writer, word: str, color: u8, use_color: bool) { + if use_color && term::color_supported() { + term::fg(out.get_buf_writer(), color); + } + out.write_str(word); + if use_color && term::color_supported() { + term::reset(out.get_buf_writer()); + } + } +} + +fn use_color() -> bool { ret get_concurrency() == 1u; } + +tag testevent<T> { + te_filtered([test_desc<T>]); + te_wait(test_desc<T>); + te_result(test_desc<T>, test_result); +} + +fn run_tests<copy T>(opts: test_opts, tests: [test_desc<T>], + to_task: test_to_task<T>, + callback: fn@(testevent<T>)) { + + let filtered_tests = filter_tests(opts, tests); + callback(te_filtered(filtered_tests)); + + // It's tempting to just spawn all the tests at once but that doesn't + // provide a great user experience because you might sit waiting for the + // result of a particular test for an unusually long amount of time. + let concurrency = get_concurrency(); + log #fmt["using %u test tasks", concurrency]; + let total = vec::len(filtered_tests); + let run_idx = 0u; + let wait_idx = 0u; + let futures = []; + + while wait_idx < total { + while vec::len(futures) < concurrency && run_idx < total { + futures += [run_test(filtered_tests[run_idx], to_task)]; + run_idx += 1u; + } + + let future = futures[0]; + callback(te_wait(future.test)); + let result = future.wait(); + callback(te_result(future.test, result)); + futures = vec::slice(futures, 1u, vec::len(futures)); + wait_idx += 1u; + } +} + +fn get_concurrency() -> uint { rustrt::sched_threads() } + +fn filter_tests<copy T>(opts: test_opts, + tests: [test_desc<T>]) -> [test_desc<T>] { + let filtered = tests; + + // Remove tests that don't match the test filter + filtered = if option::is_none(opts.filter) { + filtered + } else { + let filter_str = + alt opts.filter { + option::some(f) { f } + option::none. { "" } + }; + + fn filter_fn<copy T>(test: test_desc<T>, filter_str: str) -> + option::t<test_desc<T>> { + if str::find(test.name, filter_str) >= 0 { + ret option::some(test); + } else { ret option::none; } + } + + let filter = bind filter_fn(_, filter_str); + + vec::filter_map(filter, filtered) + }; + + // Maybe pull out the ignored test and unignore them + filtered = if !opts.run_ignored { + filtered + } else { + fn filter<copy T>(test: test_desc<T>) -> option::t<test_desc<T>> { + if test.ignore { + ret option::some({name: test.name, + fn: test.fn, + ignore: false, + should_fail: test.should_fail}); + } else { ret option::none; } + }; + + vec::filter_map(bind filter(_), filtered) + }; + + // Sort the tests alphabetically + filtered = + { + fn lteq<T>(t1: test_desc<T>, t2: test_desc<T>) -> bool { + str::lteq(t1.name, t2.name) + } + sort::merge_sort(bind lteq(_, _), filtered) + }; + + ret filtered; +} + +type test_future<T> = {test: test_desc<T>, wait: fn@() -> test_result}; + +fn run_test<copy T>(test: test_desc<T>, + to_task: test_to_task<T>) -> test_future<T> { + if test.ignore { + ret {test: test, wait: fn () -> test_result { tr_ignored }}; + } + + let test_task = to_task(test.fn); + ret {test: test, + wait: + bind fn (test_task: joinable, should_fail: bool) -> test_result { + alt task::join(test_task) { + task::tr_success. { + if should_fail { tr_failed } + else { tr_ok } + } + task::tr_failure. { + if should_fail { tr_ok } + else { tr_failed } + } + } + }(test_task, test.should_fail)}; +} + +// We need to run our tests in another task in order to trap test failures. +// This function only works with functions that don't contain closures. +fn default_test_to_task(&&f: default_test_fn) -> joinable { + fn run_task(f: default_test_fn) { + configure_test_task(); + f(); + } + ret task::spawn_joinable(copy f, run_task); +} + +// Call from within a test task to make sure it's set up correctly +fn configure_test_task() { + // If this task fails we don't want that failure to propagate to the + // test runner or else we couldn't keep running tests + task::unsupervise(); +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/time.rs b/src/libstd/time.rs new file mode 100644 index 00000000000..2865666fdc8 --- /dev/null +++ b/src/libstd/time.rs @@ -0,0 +1,30 @@ +/* +Module: time +*/ + +// FIXME: Document what these functions do + +#[abi = "cdecl"] +native mod rustrt { + fn get_time(&sec: u32, &usec: u32); + fn nano_time(&ns: u64); +} + +/* Type: timeval */ +type timeval = {sec: u32, usec: u32}; + +/* Function: get_time */ +fn get_time() -> timeval { + let sec = 0u32; + let usec = 0u32; + rustrt::get_time(sec, usec); + ret {sec: sec, usec: usec}; +} + +/* Function: precise_time_ns */ +fn precise_time_ns() -> u64 { let ns = 0u64; rustrt::nano_time(ns); ret ns; } + +/* Function: precise_time_s */ +fn precise_time_s() -> float { + ret (precise_time_ns() as float) / 1000000000.; +} diff --git a/src/libstd/treemap.rs b/src/libstd/treemap.rs new file mode 100644 index 00000000000..35d87cd518f --- /dev/null +++ b/src/libstd/treemap.rs @@ -0,0 +1,96 @@ +/* +Module: treemap + +A key,value store that works on anything. + +This works using a binary search tree. In the first version, it's a +very naive algorithm, but it will probably be updated to be a +red-black tree or something else. + +*/ + +import option::{some, none}; +import option = option::t; + +export treemap; +export init; +export insert; +export find; +export traverse; + +/* Section: Types */ + +/* +Type: treemap +*/ +type treemap<K, V> = @mutable tree_node<K, V>; + +/* +Tag: tree_node +*/ +tag tree_node<K, V> { empty; node(@K, @V, treemap<K, V>, treemap<K, V>); } + +/* Section: Operations */ + +/* +Function: init + +Create a treemap +*/ +fn init<K, V>() -> treemap<K, V> { @mutable empty } + +/* +Function: insert + +Insert a value into the map +*/ +fn insert<copy K, copy V>(m: treemap<K, V>, k: K, v: V) { + alt m { + @empty. { *m = node(@k, @v, @mutable empty, @mutable empty); } + @node(@kk, _, _, _) { + + // We have to name left and right individually, because + // otherwise the alias checker complains. + if k < kk { + alt m { @node(_, _, left, _) { insert(left, k, v); } } + } else { alt m { @node(_, _, _, right) { insert(right, k, v); } } } + } + } +} + +/* +Function: find + +Find a value based on the key +*/ +fn find<copy K, copy V>(m: treemap<K, V>, k: K) -> option<V> { + alt *m { + empty. { none } + node(@kk, @v, _, _) { + if k == kk { + some(v) + } else if k < kk { + + // Again, ugliness to unpack left and right individually. + alt *m { node(_, _, left, _) { find(left, k) } } + } else { alt *m { node(_, _, _, right) { find(right, k) } } } + } + } +} + +/* +Function: traverse + +Visit all pairs in the map in order. +*/ +fn traverse<K, V>(m: treemap<K, V>, f: block(K, V)) { + alt *m { + empty. { } + node(k, v, _, _) { + let k1 = k, v1 = v; + alt *m { node(_, _, left, _) { traverse(left, f); } } + f(*k1, *v1); + alt *m { node(_, _, _, right) { traverse(right, f); } } + } + } +} diff --git a/src/libstd/tri.rs b/src/libstd/tri.rs new file mode 100644 index 00000000000..fd4456cfde3 --- /dev/null +++ b/src/libstd/tri.rs @@ -0,0 +1,188 @@ +// -*- rust -*- + +/* +Module: tri + +ADT for the ternary Kleene logic K3 + +This allows reasoning with three logic values (true, false, unknown). + +Implementation: Truth values are represented using a single u8 and +all operations are done using bit operations which is fast +on current cpus. +*/ + +export t, true, false, unknown; +export not, and, or, xor, implies, eq, ne, is_true, is_false; +export from_str, to_str, all_values, to_bit; + +/* +Type: t + +The type of ternary logic values +*/ +type t = u8; + +const b0: u8 = 1u8; +const b1: u8 = 2u8; +const b01: u8 = 3u8; + +/* +Constant: unknown + +Logic value for unknown (maybe true xor maybe false) +*/ +const unknown: t = 0u8; + +/* +Constant: true + +Logic value for truth +*/ +const true: t = 1u8; + +/* +Constant: false + +Logic value for falsehood +*/ +const false: t = 2u8; + +/* Function: not + +Negation/Inverse +*/ +pure fn not(v: t) -> t { ((v << 1u8) | (v >> 1u8)) & b01 } + +/* Function: and + +Conjunction +*/ +pure fn and(a: t, b: t) -> t { ((a | b) & b1) | ((a & b) & b0) } + +/* Function: or + +Disjunction +*/ +pure fn or(a: t, b: t) -> t { ((a & b) & b1) | ((a | b) & b0) } + +/* +Function: xor + +Exclusive or +*/ +pure fn xor(a: t, b: t) -> t { + let anb = a & b; + let aob = a & not(b); + ret ((anb & b1) | (anb << 1u8) | (aob >> 1u8) | (aob & b0)) & b01; +} + +/* +Function: implies + +Classic implication, i.e. from `a` follows `b` +*/ +pure fn implies(a: t, b: t) -> t { + ret ((a & b1) >> 1u8) | (b & b0) | ((a << 1u8) & b & b1); +} + +/* +Predicate: eq + +Returns: + +true if truth values `a` and `b` are indistinguishable in the logic +*/ +pure fn eq(a: t, b: t) -> bool { a == b } + +/* +Predicate: ne + +Returns: + +true if truth values `a` and `b` are distinguishable in the logic +*/ +pure fn ne(a: t, b: t) -> bool { a != b } + +/* +Predicate: is_true + +Returns: + +true if `v` represents truth in the logic +*/ +pure fn is_true(v: t) -> bool { v == tri::true } + +/* +Predicate: is_false + +Returns: + +true if `v` represents false in the logic +*/ +pure fn is_false(v: t) -> bool { v == tri::false } + +/* +Predicate: is_unknown + +Returns: + +true if `v` represents the unknown state in the logic +*/ +pure fn is_unknown(v: t) -> bool { v == unknown } + +/* +Function: from_str + +Parse logic value from `s` +*/ +pure fn from_str(s: str) -> t { + alt s { + "unknown" { unknown } + "true" { tri::true } + "false" { tri::false } + } +} + +/* +Function: to_str + +Convert `v` into a string +*/ +pure fn to_str(v: t) -> str { + // FIXME replace with consts as soon as that works + alt v { + 0u8 { "unknown" } + 1u8 { "true" } + 2u8 { "false" } + } +} + +/* +Function: all_values + +Iterates over all truth values by passing them to `blk` +in an unspecified order +*/ +fn all_values(blk: block(v: t)) { + blk(tri::false); + blk(unknown); + blk(tri::true); +} + +/* +Function: to_bit + +Returns: + +An u8 whose first bit is set if `if_true(v)` holds +*/ +fn to_bit(v: t) -> u8 { v & b0 } + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/u32.rs b/src/libstd/u32.rs new file mode 100644 index 00000000000..be6f649a979 --- /dev/null +++ b/src/libstd/u32.rs @@ -0,0 +1,27 @@ +/* +Module: u32 +*/ + +/* +Const: min_value + +Return the minimal value for a u32 +*/ +const min_value: u32 = 0u32; + +/* +Const: max_value + +Return the maximal value for a u32 +*/ +const max_value: u32 = 4294967296u32; + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/libstd/u64.rs b/src/libstd/u64.rs new file mode 100644 index 00000000000..aacc35a827d --- /dev/null +++ b/src/libstd/u64.rs @@ -0,0 +1,65 @@ +/* +Module: u64 +*/ + +/* +Const: min_value + +Return the minimal value for a u64 +*/ +const min_value: u64 = 0u64; + +/* +Const: max_value + +Return the maximal value for a u64 +*/ +const max_value: u64 = 18446744073709551615u64; + +/* +Function: to_str + +Convert to a string in a given base +*/ +fn to_str(n: u64, radix: uint) -> str { + assert (0u < radix && radix <= 16u); + + let r64 = radix as u64; + + fn digit(n: u64) -> str { + ret alt n { + 0u64 { "0" } + 1u64 { "1" } + 2u64 { "2" } + 3u64 { "3" } + 4u64 { "4" } + 5u64 { "5" } + 6u64 { "6" } + 7u64 { "7" } + 8u64 { "8" } + 9u64 { "9" } + 10u64 { "a" } + 11u64 { "b" } + 12u64 { "c" } + 13u64 { "d" } + 14u64 { "e" } + 15u64 { "f" } + _ { fail } + }; + } + + if n == 0u64 { ret "0"; } + + let s = ""; + + let n = n; + while n > 0u64 { s = digit(n % r64) + s; n /= r64; } + ret s; +} + +/* +Function: str + +Convert to a string +*/ +fn str(n: u64) -> str { ret to_str(n, 10u); } diff --git a/src/libstd/u8.rs b/src/libstd/u8.rs new file mode 100644 index 00000000000..eadfcadda4a --- /dev/null +++ b/src/libstd/u8.rs @@ -0,0 +1,68 @@ +/* +Module: u8 +*/ + +/* +Const: max_value + +The maximum value of a u8. +*/ +const max_value: u8 = 255u8; + +/* +Const: min_value + +The minumum value of a u8. +*/ +const min_value: u8 = 0u8; + +/* Function: add */ +pure fn add(x: u8, y: u8) -> u8 { ret x + y; } + +/* Function: sub */ +pure fn sub(x: u8, y: u8) -> u8 { ret x - y; } + +/* Function: mul */ +pure fn mul(x: u8, y: u8) -> u8 { ret x * y; } + +/* Function: div */ +pure fn div(x: u8, y: u8) -> u8 { ret x / y; } + +/* Function: rem */ +pure fn rem(x: u8, y: u8) -> u8 { ret x % y; } + +/* Predicate: lt */ +pure fn lt(x: u8, y: u8) -> bool { ret x < y; } + +/* Predicate: le */ +pure fn le(x: u8, y: u8) -> bool { ret x <= y; } + +/* Predicate: eq */ +pure fn eq(x: u8, y: u8) -> bool { ret x == y; } + +/* Predicate: ne */ +pure fn ne(x: u8, y: u8) -> bool { ret x != y; } + +/* Predicate: ge */ +pure fn ge(x: u8, y: u8) -> bool { ret x >= y; } + +/* Predicate: gt */ +pure fn gt(x: u8, y: u8) -> bool { ret x > y; } + +/* +Function: range + +Iterate over the range [`lo`..`hi`) +*/ +fn range(lo: u8, hi: u8, it: block(u8)) { + let i = lo; + while i < hi { it(i); i += 1u8; } +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/ufind.rs b/src/libstd/ufind.rs new file mode 100644 index 00000000000..7de70526a7f --- /dev/null +++ b/src/libstd/ufind.rs @@ -0,0 +1,51 @@ + +import option::{some, none}; + + +// A very naive implementation of union-find with unsigned integer nodes. +// Maintains the invariant that the root of a node is always equal to or less +// than the node itself. +type node = option::t<uint>; + +type ufind = {mutable nodes: [mutable node]}; + +fn make() -> ufind { ret {mutable nodes: [mutable]}; } + +fn make_set(ufnd: ufind) -> uint { + let idx = vec::len(ufnd.nodes); + ufnd.nodes += [mutable none::<uint>]; + ret idx; +} + + +/// Creates sets as necessary to ensure that least `n` sets are present in the +/// data structure. +fn grow(ufnd: ufind, n: uint) { + while set_count(ufnd) < n { make_set(ufnd); } +} + +fn find(ufnd: ufind, n: uint) -> uint { + alt ufnd.nodes[n] { + none. { ret n; } + some(m) { let m_ = m; be find(ufnd, m_); } + } +} + +fn union(ufnd: ufind, m: uint, n: uint) { + let m_root = find(ufnd, m); + let n_root = find(ufnd, n); + if m_root < n_root { + ufnd.nodes[n_root] = some::<uint>(m_root); + } else if m_root > n_root { ufnd.nodes[m_root] = some::<uint>(n_root); } +} + +fn set_count(ufnd: ufind) -> uint { ret vec::len::<node>(ufnd.nodes); } + + +// Removes all sets with IDs greater than or equal to the given value. +fn prune(ufnd: ufind, n: uint) { + // TODO: Use "slice" once we get rid of "const" + + let len = vec::len::<node>(ufnd.nodes); + while len != n { vec::pop::<node>(ufnd.nodes); len -= 1u; } +} diff --git a/src/libstd/uint.rs b/src/libstd/uint.rs new file mode 100644 index 00000000000..ae1ba628aa6 --- /dev/null +++ b/src/libstd/uint.rs @@ -0,0 +1,254 @@ +/* +Module: uint +*/ + +/* +Const: min_value + +Return the minimal value for an uint. + +This is always 0 +*/ +const min_value: uint = 0u; + +/* +Const: max_value + +Return the maximal value for an uint. + +This is 2^wordsize - 1 +*/ +const max_value: uint = 0u - 1u; + +/* Function: add */ +pure fn add(x: uint, y: uint) -> uint { ret x + y; } + +/* Function: sub */ +pure fn sub(x: uint, y: uint) -> uint { ret x - y; } + +/* Function: mul */ +pure fn mul(x: uint, y: uint) -> uint { ret x * y; } + +/* Function: div */ +pure fn div(x: uint, y: uint) -> uint { ret x / y; } + +/* Function: div_ceil + + Divide two numbers, return the result, rounded up. + + Parameters: + x - an integer + y - an integer distinct from 0u + + Return: + The smallest integer `q` such that `x/y <= q`. +*/ +pure fn div_ceil(x: uint, y: uint) -> uint { + let div = div(x, y); + if x % y == 0u { ret div;} + else { ret div + 1u; } +} + +/* Function: div_ceil + + Divide two numbers, return the result, rounded to the closest integer. + + Parameters: + x - an integer + y - an integer distinct from 0u + + Return: + The integer `q` closest to `x/y`. +*/ +pure fn div_round(x: uint, y: uint) -> uint { + let div = div(x, y); + if x % y * 2u < y { ret div;} + else { ret div + 1u; } +} + +/* Function: div_ceil + + Divide two numbers, return the result, rounded down. + + Parameters: + x - an integer + y - an integer distinct from 0u + + Note: This is the same function as `div`. + + Return: + The smallest integer `q` such that `x/y <= q`. This + is either `x/y` or `x/y + 1`. +*/ +pure fn div_floor(x: uint, y: uint) -> uint { ret x / y; } + +/* Function: rem */ +pure fn rem(x: uint, y: uint) -> uint { ret x % y; } + +/* Predicate: lt */ +pure fn lt(x: uint, y: uint) -> bool { ret x < y; } + +/* Predicate: le */ +pure fn le(x: uint, y: uint) -> bool { ret x <= y; } + +/* Predicate: eq */ +pure fn eq(x: uint, y: uint) -> bool { ret x == y; } + +/* Predicate: ne */ +pure fn ne(x: uint, y: uint) -> bool { ret x != y; } + +/* Predicate: ge */ +pure fn ge(x: uint, y: uint) -> bool { ret x >= y; } + +/* Predicate: gt */ +pure fn gt(x: uint, y: uint) -> bool { ret x > y; } + +/* +Function: range + +Iterate over the range [`lo`..`hi`) +*/ +fn range(lo: uint, hi: uint, it: block(uint)) { + let i = lo; + while i < hi { it(i); i += 1u; } +} + +/* +Function: loop + +Iterate over the range [`lo`..`hi`), or stop when requested + +Parameters: +lo - The integer at which to start the loop (included) +hi - The integer at which to stop the loop (excluded) +it - A block to execute with each consecutive integer of the range. +Return `true` to continue, `false` to stop. + +Returns: + +`true` If execution proceeded correctly, `false` if it was interrupted, +that is if `it` returned `false` at any point. +*/ +fn loop(lo: uint, hi: uint, it: block(uint) -> bool) -> bool { + let i = lo; + while i < hi { + if (!it(i)) { ret false; } + i += 1u; + } + ret true; +} + +/* +Function: next_power_of_two + +Returns the smallest power of 2 greater than or equal to `n` +*/ +fn next_power_of_two(n: uint) -> uint { + let halfbits: uint = sys::size_of::<uint>() * 4u; + let tmp: uint = n - 1u; + let shift: uint = 1u; + while shift <= halfbits { tmp |= tmp >> shift; shift <<= 1u; } + ret tmp + 1u; +} + +/* +Function: parse_buf + +Parse a buffer of bytes + +Parameters: + +buf - A byte buffer +radix - The base of the number + +Failure: + +buf must not be empty +*/ +fn parse_buf(buf: [u8], radix: uint) -> uint { + if vec::len::<u8>(buf) == 0u { + log_err "parse_buf(): buf is empty"; + fail; + } + let i = vec::len::<u8>(buf) - 1u; + let power = 1u; + let n = 0u; + while true { + let digit = char::to_digit(buf[i] as char); + if (digit as uint) >= radix { + fail; + } + n += (digit as uint) * power; + power *= radix; + if i == 0u { ret n; } + i -= 1u; + } + fail; +} + +/* +Function: from_str + +Parse a string to an int + +Failure: + +s must not be empty +*/ +fn from_str(s: str) -> uint { parse_buf(str::bytes(s), 10u) } + +/* +Function: to_str + +Convert to a string in a given base +*/ +fn to_str(num: uint, radix: uint) -> str { + let n = num; + assert (0u < radix && radix <= 16u); + fn digit(n: uint) -> char { + ret alt n { + 0u { '0' } + 1u { '1' } + 2u { '2' } + 3u { '3' } + 4u { '4' } + 5u { '5' } + 6u { '6' } + 7u { '7' } + 8u { '8' } + 9u { '9' } + 10u { 'a' } + 11u { 'b' } + 12u { 'c' } + 13u { 'd' } + 14u { 'e' } + 15u { 'f' } + _ { fail } + }; + } + if n == 0u { ret "0"; } + let s: str = ""; + while n != 0u { + s += str::unsafe_from_byte(digit(n % radix) as u8); + n /= radix; + } + let s1: str = ""; + let len: uint = str::byte_len(s); + while len != 0u { len -= 1u; s1 += str::unsafe_from_byte(s[len]); } + ret s1; +} + +/* +Function: str + +Convert to a string +*/ +fn str(i: uint) -> str { ret to_str(i, 10u); } + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/unicode.rs b/src/libstd/unicode.rs new file mode 100644 index 00000000000..5038721d26e --- /dev/null +++ b/src/libstd/unicode.rs @@ -0,0 +1,166 @@ + +mod icu { + type UBool = u8; + type UProperty = int; + type UChar32 = char; + + const TRUE : u8 = 1u8; + const FALSE : u8 = 1u8; + + const UCHAR_ALPHABETIC : UProperty = 0; + const UCHAR_BINARY_START : UProperty = 0; // = UCHAR_ALPHABETIC + const UCHAR_ASCII_HEX_DIGIT : UProperty = 1; + const UCHAR_BIDI_CONTROL : UProperty = 2; + + const UCHAR_BIDI_MIRRORED : UProperty = 3; + const UCHAR_DASH : UProperty = 4; + const UCHAR_DEFAULT_IGNORABLE_CODE_POINT : UProperty = 5; + const UCHAR_DEPRECATED : UProperty = 6; + + const UCHAR_DIACRITIC : UProperty = 7; + const UCHAR_EXTENDER : UProperty = 8; + const UCHAR_FULL_COMPOSITION_EXCLUSION : UProperty = 9; + const UCHAR_GRAPHEME_BASE : UProperty = 10; + + const UCHAR_GRAPHEME_EXTEND : UProperty = 11; + const UCHAR_GRAPHEME_LINK : UProperty = 12; + const UCHAR_HEX_DIGIT : UProperty = 13; + const UCHAR_HYPHEN : UProperty = 14; + + const UCHAR_ID_CONTINUE : UProperty = 15; + const UCHAR_ID_START : UProperty = 16; + const UCHAR_IDEOGRAPHIC : UProperty = 17; + const UCHAR_IDS_BINARY_OPERATOR : UProperty = 18; + + const UCHAR_IDS_TRINARY_OPERATOR : UProperty = 19; + const UCHAR_JOIN_CONTROL : UProperty = 20; + const UCHAR_LOGICAL_ORDER_EXCEPTION : UProperty = 21; + const UCHAR_LOWERCASE : UProperty = 22; + + const UCHAR_MATH : UProperty = 23; + const UCHAR_NONCHARACTER_CODE_POINT : UProperty = 24; + const UCHAR_QUOTATION_MARK : UProperty = 25; + const UCHAR_RADICAL : UProperty = 26; + + const UCHAR_SOFT_DOTTED : UProperty = 27; + const UCHAR_TERMINAL_PUNCTUATION : UProperty = 28; + const UCHAR_UNIFIED_IDEOGRAPH : UProperty = 29; + const UCHAR_UPPERCASE : UProperty = 30; + + const UCHAR_WHITE_SPACE : UProperty = 31; + const UCHAR_XID_CONTINUE : UProperty = 32; + const UCHAR_XID_START : UProperty = 33; + const UCHAR_CASE_SENSITIVE : UProperty = 34; + + const UCHAR_S_TERM : UProperty = 35; + const UCHAR_VARIATION_SELECTOR : UProperty = 36; + const UCHAR_NFD_INERT : UProperty = 37; + const UCHAR_NFKD_INERT : UProperty = 38; + + const UCHAR_NFC_INERT : UProperty = 39; + const UCHAR_NFKC_INERT : UProperty = 40; + const UCHAR_SEGMENT_STARTER : UProperty = 41; + const UCHAR_PATTERN_SYNTAX : UProperty = 42; + + const UCHAR_PATTERN_WHITE_SPACE : UProperty = 43; + const UCHAR_POSIX_ALNUM : UProperty = 44; + const UCHAR_POSIX_BLANK : UProperty = 45; + const UCHAR_POSIX_GRAPH : UProperty = 46; + + const UCHAR_POSIX_PRINT : UProperty = 47; + const UCHAR_POSIX_XDIGIT : UProperty = 48; + const UCHAR_CASED : UProperty = 49; + const UCHAR_CASE_IGNORABLE : UProperty = 50; + + const UCHAR_CHANGES_WHEN_LOWERCASED : UProperty = 51; + const UCHAR_CHANGES_WHEN_UPPERCASED : UProperty = 52; + const UCHAR_CHANGES_WHEN_TITLECASED : UProperty = 53; + const UCHAR_CHANGES_WHEN_CASEFOLDED : UProperty = 54; + + const UCHAR_CHANGES_WHEN_CASEMAPPED : UProperty = 55; + const UCHAR_CHANGES_WHEN_NFKC_CASEFOLDED : UProperty = 56; + const UCHAR_BINARY_LIMIT : UProperty = 57; + const UCHAR_BIDI_CLASS : UProperty = 0x1000; + + const UCHAR_INT_START : UProperty = 0x1000; // UCHAR_BIDI_CLASS + const UCHAR_BLOCK : UProperty = 0x1001; + const UCHAR_CANONICAL_COMBINING_CLASS : UProperty = 0x1002; + const UCHAR_DECOMPOSITION_TYPE : UProperty = 0x1003; + + const UCHAR_EAST_ASIAN_WIDTH : UProperty = 0x1004; + const UCHAR_GENERAL_CATEGORY : UProperty = 0x1005; + const UCHAR_JOINING_GROUP : UProperty = 0x1006; + const UCHAR_JOINING_TYPE : UProperty = 0x1007; + + const UCHAR_LINE_BREAK : UProperty = 0x1008; + const UCHAR_NUMERIC_TYPE : UProperty = 0x1009; + const UCHAR_SCRIPT : UProperty = 0x100A; + const UCHAR_HANGUL_SYLLABLE_TYPE : UProperty = 0x100B; + + const UCHAR_NFD_QUICK_CHECK : UProperty = 0x100C; + const UCHAR_NFKD_QUICK_CHECK : UProperty = 0x100D; + const UCHAR_NFC_QUICK_CHECK : UProperty = 0x100E; + const UCHAR_NFKC_QUICK_CHECK : UProperty = 0x100F; + + const UCHAR_LEAD_CANONICAL_COMBINING_CLASS : UProperty = 0x1010; + const UCHAR_TRAIL_CANONICAL_COMBINING_CLASS : UProperty = 0x1011; + const UCHAR_GRAPHEME_CLUSTER_BREAK : UProperty = 0x1012; + const UCHAR_SENTENCE_BREAK : UProperty = 0x1013; + + const UCHAR_WORD_BREAK : UProperty = 0x1014; + const UCHAR_INT_LIMIT : UProperty = 0x1015; + + const UCHAR_GENERAL_CATEGORY_MASK : UProperty = 0x2000; + const UCHAR_MASK_START : UProperty = 0x2000; + // = UCHAR_GENERAL_CATEGORY_MASK + const UCHAR_MASK_LIMIT : UProperty = 0x2001; + + const UCHAR_NUMERIC_VALUE : UProperty = 0x3000; + const UCHAR_DOUBLE_START : UProperty = 0x3000; + // = UCHAR_NUMERIC_VALUE + const UCHAR_DOUBLE_LIMIT : UProperty = 0x3001; + + const UCHAR_AGE : UProperty = 0x4000; + const UCHAR_STRING_START : UProperty = 0x4000; // = UCHAR_AGE + const UCHAR_BIDI_MIRRORING_GLYPH : UProperty = 0x4001; + const UCHAR_CASE_FOLDING : UProperty = 0x4002; + + const UCHAR_ISO_COMMENT : UProperty = 0x4003; + const UCHAR_LOWERCASE_MAPPING : UProperty = 0x4004; + const UCHAR_NAME : UProperty = 0x4005; + const UCHAR_SIMPLE_CASE_FOLDING : UProperty = 0x4006; + + const UCHAR_SIMPLE_LOWERCASE_MAPPING : UProperty = 0x4007; + const UCHAR_SIMPLE_TITLECASE_MAPPING : UProperty = 0x4008; + const UCHAR_SIMPLE_UPPERCASE_MAPPING : UProperty = 0x4009; + const UCHAR_TITLECASE_MAPPING : UProperty = 0x400A; + + const UCHAR_UNICODE_1_NAME : UProperty = 0x400B; + const UCHAR_UPPERCASE_MAPPING : UProperty = 0x400C; + const UCHAR_STRING_LIMIT : UProperty = 0x400D; + + const UCHAR_SCRIPT_EXTENSIONS : UProperty = 0x7000; + const UCHAR_OTHER_PROPERTY_START : UProperty = 0x7000; + // = UCHAR_SCRIPT_EXTENSIONS; + const UCHAR_OTHER_PROPERTY_LIMIT : UProperty = 0x7001; + + const UCHAR_INVALID_CODE : UProperty = 0xffffffff; + // FIXME: should be -1, change when compiler supports negative + // constants + + #[link_name = "icuuc"] + #[abi = "cdecl"] + native mod libicu { + fn u_hasBinaryProperty(c: UChar32, which: UProperty) -> UBool; + } +} + +fn is_XID_start(c: char) -> bool { + ret icu::libicu::u_hasBinaryProperty(c, icu::UCHAR_XID_START) + == icu::TRUE; +} + +fn is_XID_continue(c: char) -> bool { + ret icu::libicu::u_hasBinaryProperty(c, icu::UCHAR_XID_START) + == icu::TRUE; +} diff --git a/src/libstd/unsafe.rs b/src/libstd/unsafe.rs new file mode 100644 index 00000000000..22cce495649 --- /dev/null +++ b/src/libstd/unsafe.rs @@ -0,0 +1,41 @@ +/* +Module: unsafe + +Unsafe operations +*/ + +#[abi = "rust-intrinsic"] +native mod rusti { + fn cast<T, U>(src: T) -> U; +} + +#[abi = "cdecl"] +native mod rustrt { + fn leak<T>(-thing: T); +} + +/* +Function: reinterpret_cast + +Casts the value at `src` to U. The two types must have the same length. +*/ +unsafe fn reinterpret_cast<T, U>(src: T) -> U { + let t1 = sys::get_type_desc::<T>(); + let t2 = sys::get_type_desc::<U>(); + if (*t1).size != (*t2).size { + fail "attempt to cast values of differing sizes"; + } + ret rusti::cast(src); +} + +/* +Function: leak + +Move `thing` into the void. + +The leak function will take ownership of the provided value but neglect +to run any required cleanup or memory-management operations on it. This +can be used for various acts of magick, particularly when using +reinterpret_cast on managed pointer types. +*/ +unsafe fn leak<T>(-thing: T) { rustrt::leak(thing); } diff --git a/src/libstd/util.rs b/src/libstd/util.rs new file mode 100644 index 00000000000..a15b5291546 --- /dev/null +++ b/src/libstd/util.rs @@ -0,0 +1,57 @@ +/* +Module: util +*/ + +/* +Function: id + +The identity function +*/ +pure fn id<T>(x: T) -> T { x } + +/* +Function: unreachable + +A standard function to use to indicate unreachable code. Because the +function is guaranteed to fail typestate will correctly identify +any code paths following the appearance of this function as unreachable. +*/ +fn unreachable() -> ! { + fail "Internal error: entered unreachable code"; +} + +/* FIXME (issue #141): See test/run-pass/constrained-type.rs. Uncomment + * the constraint once fixed. */ +/* +Function: rational + +A rational number +*/ +type rational = {num: int, den: int}; // : int::positive(*.den); + +/* +Function: rational_leq +*/ +pure fn rational_leq(x: rational, y: rational) -> bool { + // NB: Uses the fact that rationals have positive denominators WLOG: + + x.num * y.den <= y.num * x.den +} + +/* +Function: orb +*/ +pure fn orb(a: bool, b: bool) -> bool { a || b } + +// FIXME: Document what this is for or delete it +tag void { + void(@void); +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/uv.rs b/src/libstd/uv.rs new file mode 100644 index 00000000000..17916b844e3 --- /dev/null +++ b/src/libstd/uv.rs @@ -0,0 +1,150 @@ +/* +This is intended to be a low-level binding to libuv that very closely mimics +the C libuv API. Does very little right now pending scheduler improvements. +*/ + +#[cfg(target_os = "linux")]; +#[cfg(target_os = "macos")]; + +export sanity_check; +export loop_t, idle_t; +export loop_new, loop_delete, default_loop, run, unref; +export idle_init, idle_start; +export idle_new; + +#[link_name = "rustrt"] +native mod uv { + fn rust_uv_loop_new() -> *loop_t; + fn rust_uv_loop_delete(loop: *loop_t); + fn rust_uv_default_loop() -> *loop_t; + fn rust_uv_run(loop: *loop_t) -> ctypes::c_int; + fn rust_uv_unref(loop: *loop_t); + fn rust_uv_idle_init(loop: *loop_t, idle: *idle_t) -> ctypes::c_int; + fn rust_uv_idle_start(idle: *idle_t, cb: idle_cb) -> ctypes::c_int; +} + +#[link_name = "rustrt"] +native mod helpers { + fn rust_uv_size_of_idle_t() -> ctypes::size_t; +} + +type opaque_cb = *ctypes::void; + +type handle_type = ctypes::enum; + +type close_cb = opaque_cb; +type idle_cb = opaque_cb; + +#[cfg(target_os = "linux")] +#[cfg(target_os = "macos")] +type handle_private_fields = { + a00: ctypes::c_int, + a01: ctypes::c_int, + a02: ctypes::c_int, + a03: ctypes::c_int, + a04: ctypes::c_int, + a05: ctypes::c_int, + a06: int, + a07: int, + a08: int, + a09: int, + a10: int, + a11: int, + a12: int +}; + +type handle_fields = { + loop: *loop_t, + type_: handle_type, + close_cb: close_cb, + data: *ctypes::void, + private: handle_private_fields +}; + +type handle_t = { + fields: handle_fields +}; + +type loop_t = int; + + + + +type idle_t = { + fields: handle_fields + /* private: idle_private_fields */ +}; + +fn idle_init(loop: *loop_t, idle: *idle_t) -> ctypes::c_int { + uv::rust_uv_idle_init(loop, idle) +} + +fn idle_start(idle: *idle_t, cb: idle_cb) -> ctypes::c_int { + uv::rust_uv_idle_start(idle, cb) +} + + + + +fn default_loop() -> *loop_t { + uv::rust_uv_default_loop() +} + +fn loop_new() -> *loop_t { + uv::rust_uv_loop_new() +} + +fn loop_delete(loop: *loop_t) { + uv::rust_uv_loop_delete(loop) +} + +fn run(loop: *loop_t) -> ctypes::c_int { + uv::rust_uv_run(loop) +} + +fn unref(loop: *loop_t) { + uv::rust_uv_unref(loop) +} + + +fn sanity_check() { + fn check_size(t: str, uv: ctypes::size_t, rust: ctypes::size_t) { + log #fmt("size of %s: uv: %u, rust: %u", t, uv, rust); + assert uv == rust; + } + check_size("idle_t", + helpers::rust_uv_size_of_idle_t(), + sys::size_of::<idle_t>()); +} + +#[cfg(target_os = "linux")] +#[cfg(target_os = "macos")] +fn handle_fields_new() -> handle_fields { + { + loop: ptr::null(), + type_: 0u32, + close_cb: ptr::null(), + data: ptr::null(), + private: { + a00: 0i32, + a01: 0i32, + a02: 0i32, + a03: 0i32, + a04: 0i32, + a05: 0i32, + a06: 0, + a07: 0, + a08: 0, + a09: 0, + a10: 0, + a11: 0, + a12: 0 + } + } +} + +fn idle_new() -> idle_t { + { + fields: handle_fields_new() + } +} \ No newline at end of file diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs new file mode 100644 index 00000000000..d8f89b3b27b --- /dev/null +++ b/src/libstd/vec.rs @@ -0,0 +1,835 @@ +/* +Module: vec +*/ + +import option::{some, none}; +import uint::next_power_of_two; +import ptr::addr_of; + +#[abi = "rust-intrinsic"] +native mod rusti { + fn vec_len<T>(&&v: [const T]) -> uint; +} + +#[abi = "cdecl"] +native mod rustrt { + fn vec_reserve_shared<T>(t: *sys::type_desc, + &v: [const T], + n: uint); + fn vec_from_buf_shared<T>(t: *sys::type_desc, + ptr: *T, + count: uint) -> [T]; +} + +/* +Type: init_op + +A function used to initialize the elements of a vector. +*/ +type init_op<T> = block(uint) -> T; + + +/* +Predicate: is_empty + +Returns true if a vector contains no elements. +*/ +pure fn is_empty<T>(v: [const T]) -> bool { + // FIXME: This would be easier if we could just call len + for t: T in v { ret false; } + ret true; +} + +/* +Predicate: is_not_empty + +Returns true if a vector contains some elements. +*/ +pure fn is_not_empty<T>(v: [const T]) -> bool { ret !is_empty(v); } + +/* +Predicate: same_length + +Returns true if two vectors have the same length +*/ +pure fn same_length<T, U>(xs: [T], ys: [U]) -> bool { + vec::len(xs) == vec::len(ys) +} + +/* +Function: reserve + +Reserves capacity for `n` elements in the given vector. + +If the capacity for `v` is already equal to or greater than the requested +capacity, then no action is taken. + +Parameters: + +v - A vector +n - The number of elements to reserve space for +*/ +fn reserve<T>(&v: [const T], n: uint) { + rustrt::vec_reserve_shared(sys::get_type_desc::<T>(), v, n); +} + +/* +Function: len + +Returns the length of a vector +*/ +pure fn len<T>(v: [const T]) -> uint { unchecked { rusti::vec_len(v) } } + +/* +Function: init_fn + +Creates and initializes an immutable vector. + +Creates an immutable vector of size `n_elts` and initializes the elements +to the value returned by the function `op`. +*/ +fn init_fn<T>(op: init_op<T>, n_elts: uint) -> [T] { + let v = []; + reserve(v, n_elts); + let i: uint = 0u; + while i < n_elts { v += [op(i)]; i += 1u; } + ret v; +} + +// TODO: Remove me once we have slots. +/* +Function: init_fn_mut + +Creates and initializes a mutable vector. + +Creates a mutable vector of size `n_elts` and initializes the elements to +the value returned by the function `op`. +*/ +fn init_fn_mut<T>(op: init_op<T>, n_elts: uint) -> [mutable T] { + let v = [mutable]; + reserve(v, n_elts); + let i: uint = 0u; + while i < n_elts { v += [mutable op(i)]; i += 1u; } + ret v; +} + +/* +Function: init_elt + +Creates and initializes an immutable vector. + +Creates an immutable vector of size `n_elts` and initializes the elements +to the value `t`. +*/ +fn init_elt<copy T>(t: T, n_elts: uint) -> [T] { + let v = []; + reserve(v, n_elts); + let i: uint = 0u; + while i < n_elts { v += [t]; i += 1u; } + ret v; +} + +// TODO: Remove me once we have slots. +/* +Function: init_elt_mut + +Creates and initializes a mutable vector. + +Creates a mutable vector of size `n_elts` and initializes the elements +to the value `t`. +*/ +fn init_elt_mut<copy T>(t: T, n_elts: uint) -> [mutable T] { + let v = [mutable]; + reserve(v, n_elts); + let i: uint = 0u; + while i < n_elts { v += [mutable t]; i += 1u; } + ret v; +} + +// FIXME: Possible typestate postcondition: +// len(result) == len(v) (needs issue #586) +/* +Function: to_mut + +Produces a mutable vector from an immutable vector. +*/ +fn to_mut<copy T>(v: [T]) -> [mutable T] { + let vres = [mutable]; + for t: T in v { vres += [mutable t]; } + ret vres; +} + +// Same comment as from_mut +/* +Function: from_mut + +Produces an immutable vector from a mutable vector. +*/ +fn from_mut<copy T>(v: [mutable T]) -> [T] { + let vres = []; + for t: T in v { vres += [t]; } + ret vres; +} + +// Accessors + +/* +Function: head + +Returns the first element of a vector + +Predicates: +<is_not_empty> (v) +*/ +fn head<copy T>(v: [const T]) : is_not_empty(v) -> T { ret v[0]; } + +/* +Function: tail + +Returns all but the first element of a vector + +Predicates: +<is_not_empty> (v) +*/ +fn tail<copy T>(v: [const T]) : is_not_empty(v) -> [T] { + ret slice(v, 1u, len(v)); +} + +// FIXME: This name is sort of confusing next to init_fn, etc +// but this is the name haskell uses for this function, +// along with head/tail/last. +/* +Function: init + +Returns all but the last elemnt of a vector + +Preconditions: +`v` is not empty +*/ +fn init<copy T>(v: [const T]) -> [T] { + assert len(v) != 0u; + slice(v, 0u, len(v) - 1u) +} + +/* +Function: last + +Returns the last element of a vector + +Returns: + +An option containing the last element of `v` if `v` is not empty, or +none if `v` is empty. +*/ +fn last<copy T>(v: [const T]) -> option::t<T> { + if len(v) == 0u { ret none; } + ret some(v[len(v) - 1u]); +} + +/* +Function: last_total + +Returns the last element of a non-empty vector `v` + +Predicates: +<is_not_empty> (v) +*/ +fn last_total<copy T>(v: [const T]) : is_not_empty(v) -> T { + ret v[len(v) - 1u]; +} + +/* +Function: slice + +Returns a copy of the elements from [`start`..`end`) from `v`. +*/ +fn slice<copy T>(v: [const T], start: uint, end: uint) -> [T] { + assert (start <= end); + assert (end <= len(v)); + let result = []; + reserve(result, end - start); + let i = start; + while i < end { result += [v[i]]; i += 1u; } + ret result; +} + +// TODO: Remove me once we have slots. +/* +Function: slice_mut + +Returns a copy of the elements from [`start`..`end`) from `v`. +*/ +fn slice_mut<copy T>(v: [const T], start: uint, end: uint) -> [mutable T] { + assert (start <= end); + assert (end <= len(v)); + let result = [mutable]; + reserve(result, end - start); + let i = start; + while i < end { result += [mutable v[i]]; i += 1u; } + ret result; +} + + +// Mutators + +/* +Function: shift + +Removes the first element from a vector and return it +*/ +fn shift<copy T>(&v: [const T]) -> T { + let ln = len::<T>(v); + assert (ln > 0u); + let e = v[0]; + v = slice::<T>(v, 1u, ln); + ret e; +} + +// TODO: Write this, unsafely, in a way that's not O(n). +/* +Function: pop + +Remove the last element from a vector and return it +*/ +fn pop<copy T>(&v: [const T]) -> T { + let ln = len(v); + assert (ln > 0u); + ln -= 1u; + let e = v[ln]; + v = slice(v, 0u, ln); + ret e; +} + +// TODO: More. + + +// Appending + +/* +Function: grow + +Expands a vector in place, initializing the new elements to a given value + +Parameters: + +v - The vector to grow +n - The number of elements to add +initval - The value for the new elements +*/ +fn grow<copy T>(&v: [T], n: uint, initval: T) { + reserve(v, next_power_of_two(len(v) + n)); + let i: uint = 0u; + while i < n { v += [initval]; i += 1u; } +} + +// TODO: Remove me once we have slots. +// FIXME: Can't grow take a [const T] +/* +Function: grow_mut + +Expands a vector in place, initializing the new elements to a given value + +Parameters: + +v - The vector to grow +n - The number of elements to add +initval - The value for the new elements +*/ +fn grow_mut<copy T>(&v: [mutable T], n: uint, initval: T) { + reserve(v, next_power_of_two(len(v) + n)); + let i: uint = 0u; + while i < n { v += [mutable initval]; i += 1u; } +} + +/* +Function: grow_fn + +Expands a vector in place, initializing the new elements to the result of a +function + +Function `init_fn` is called `n` times with the values [0..`n`) + +Parameters: + +v - The vector to grow +n - The number of elements to add +init_fn - A function to call to retreive each appended element's value +*/ +fn grow_fn<T>(&v: [T], n: uint, op: init_op<T>) { + reserve(v, next_power_of_two(len(v) + n)); + let i: uint = 0u; + while i < n { v += [op(i)]; i += 1u; } +} + +/* +Function: grow_set + +Sets the value of a vector element at a given index, growing the vector as +needed + +Sets the element at position `index` to `val`. If `index` is past the end +of the vector, expands the vector by replicating `initval` to fill the +intervening space. +*/ +fn grow_set<copy T>(&v: [mutable T], index: uint, initval: T, val: T) { + if index >= len(v) { grow_mut(v, index - len(v) + 1u, initval); } + v[index] = val; +} + + +// Functional utilities + +/* +Function: map + +Apply a function to each element of a vector and return the results +*/ +fn map<T, U>(f: block(T) -> U, v: [T]) -> [U] { + let result = []; + reserve(result, len(v)); + for elem: T in v { result += [f(elem)]; } + ret result; +} + +/* +Function: map_mut + +Apply a function to each element of a mutable vector and return the results +*/ +fn map_mut<copy T, U>(f: block(T) -> U, v: [const T]) -> [U] { + let result = []; + reserve(result, len(v)); + for elem: T in v { + // copy satisfies alias checker + result += [f(copy elem)]; + } + ret result; +} + +/* +Function: map2 + +Apply a function to each pair of elements and return the results +*/ +fn map2<copy T, copy U, V>(f: block(T, U) -> V, v0: [T], v1: [U]) -> [V] { + let v0_len = len(v0); + if v0_len != len(v1) { fail; } + let u: [V] = []; + let i = 0u; + while i < v0_len { u += [f(copy v0[i], copy v1[i])]; i += 1u; } + ret u; +} + +/* +Function: filter_map + +Apply a function to each element of a vector and return the results + +If function `f` returns `none` then that element is excluded from +the resulting vector. +*/ +fn filter_map<copy T, copy U>(f: block(T) -> option::t<U>, v: [const T]) + -> [U] { + let result = []; + for elem: T in v { + alt f(copy elem) { + none. {/* no-op */ } + some(result_elem) { result += [result_elem]; } + } + } + ret result; +} + +/* +Function: filter + +Construct a new vector from the elements of a vector for which some predicate +holds. + +Apply function `f` to each element of `v` and return a vector containing +only those elements for which `f` returned true. +*/ +fn filter<copy T>(f: block(T) -> bool, v: [T]) -> [T] { + let result = []; + for elem: T in v { + if f(elem) { result += [elem]; } + } + ret result; +} + +/* +Function: concat + +Concatenate a vector of vectors. Flattens a vector of vectors of T into +a single vector of T. +*/ +fn concat<copy T>(v: [const [const T]]) -> [T] { + let new: [T] = []; + for inner: [T] in v { new += inner; } + ret new; +} + +/* +Function: foldl + +Reduce a vector from left to right +*/ +fn foldl<copy T, U>(p: block(T, U) -> T, z: T, v: [const U]) -> T { + let accum = z; + iter(v) { |elt| + accum = p(accum, elt); + } + ret accum; +} + +/* +Function: foldr + +Reduce a vector from right to left +*/ +fn foldr<T, copy U>(p: block(T, U) -> U, z: U, v: [const T]) -> U { + let accum = z; + riter(v) { |elt| + accum = p(elt, accum); + } + ret accum; +} + +/* +Function: any + +Return true if a predicate matches any elements + +If the vector contains no elements then false is returned. +*/ +fn any<T>(f: block(T) -> bool, v: [T]) -> bool { + for elem: T in v { if f(elem) { ret true; } } + ret false; +} + +/* +Function: all + +Return true if a predicate matches all elements + +If the vector contains no elements then true is returned. +*/ +fn all<T>(f: block(T) -> bool, v: [T]) -> bool { + for elem: T in v { if !f(elem) { ret false; } } + ret true; +} + +/* +Function: member + +Return true if a vector contains an element with the given value +*/ +fn member<T>(x: T, v: [T]) -> bool { + for elt: T in v { if x == elt { ret true; } } + ret false; +} + +/* +Function: count + +Returns the number of elements that are equal to a given value +*/ +fn count<T>(x: T, v: [const T]) -> uint { + let cnt = 0u; + for elt: T in v { if x == elt { cnt += 1u; } } + ret cnt; +} + +/* +Function: find + +Search for an element that matches a given predicate + +Apply function `f` to each element of `v`, starting from the first. +When function `f` returns true then an option containing the element +is returned. If `f` matches no elements then none is returned. +*/ +fn find<copy T>(f: block(T) -> bool, v: [T]) -> option::t<T> { + for elt: T in v { if f(elt) { ret some(elt); } } + ret none; +} + +/* +Function: position + +Find the first index containing a matching value + +Returns: + +option::some(uint) - The first index containing a matching value +option::none - No elements matched +*/ +fn position<T>(x: T, v: [T]) -> option::t<uint> { + let i: uint = 0u; + while i < len(v) { if x == v[i] { ret some::<uint>(i); } i += 1u; } + ret none; +} + +/* +Function: position_pred + +Find the first index for which the value matches some predicate +*/ +fn position_pred<T>(f: block(T) -> bool, v: [T]) -> option::t<uint> { + let i: uint = 0u; + while i < len(v) { if f(v[i]) { ret some::<uint>(i); } i += 1u; } + ret none; +} + +// FIXME: if issue #586 gets implemented, could have a postcondition +// saying the two result lists have the same length -- or, could +// return a nominal record with a constraint saying that, instead of +// returning a tuple (contingent on issue #869) +/* +Function: unzip + +Convert a vector of pairs into a pair of vectors + +Returns a tuple containing two vectors where the i-th element of the first +vector contains the first element of the i-th tuple of the input vector, +and the i-th element of the second vector contains the second element +of the i-th tuple of the input vector. +*/ +fn unzip<copy T, copy U>(v: [(T, U)]) -> ([T], [U]) { + let as = [], bs = []; + for (a, b) in v { as += [a]; bs += [b]; } + ret (as, bs); +} + +/* +Function: zip + +Convert two vectors to a vector of pairs + +Returns a vector of tuples, where the i-th tuple contains contains the +i-th elements from each of the input vectors. + +Preconditions: + +<same_length> (v, u) +*/ +fn zip<copy T, copy U>(v: [T], u: [U]) : same_length(v, u) -> [(T, U)] { + let zipped = []; + let sz = len(v), i = 0u; + assert (sz == len(u)); + while i < sz { zipped += [(v[i], u[i])]; i += 1u; } + ret zipped; +} + +/* +Function: swap + +Swaps two elements in a vector + +Parameters: +v - The input vector +a - The index of the first element +b - The index of the second element +*/ +fn swap<T>(v: [mutable T], a: uint, b: uint) { + v[a] <-> v[b]; +} + +/* +Function: reverse + +Reverse the order of elements in a vector, in place +*/ +fn reverse<T>(v: [mutable T]) { + let i: uint = 0u; + let ln = len::<T>(v); + while i < ln / 2u { v[i] <-> v[ln - i - 1u]; i += 1u; } +} + + +/* +Function: reversed + +Returns a vector with the order of elements reversed +*/ +fn reversed<copy T>(v: [const T]) -> [T] { + let rs: [T] = []; + let i = len::<T>(v); + if i == 0u { ret rs; } else { i -= 1u; } + while i != 0u { rs += [v[i]]; i -= 1u; } + rs += [v[0]]; + ret rs; +} + +// FIXME: Seems like this should take char params. Maybe belongs in char +/* +Function: enum_chars + +Returns a vector containing a range of chars +*/ +fn enum_chars(start: u8, end: u8) : u8::le(start, end) -> [char] { + let i = start; + let r = []; + while i <= end { r += [i as char]; i += 1u as u8; } + ret r; +} + +// FIXME: Probably belongs in uint. Compare to uint::range +/* +Function: enum_uints + +Returns a vector containing a range of uints +*/ +fn enum_uints(start: uint, end: uint) : uint::le(start, end) -> [uint] { + let i = start; + let r = []; + while i <= end { r += [i]; i += 1u; } + ret r; +} + +/* +Function: iter + +Iterates over a vector + +Iterates over vector `v` and, for each element, calls function `f` with the +element's value. + +*/ +fn iter<T>(v: [const T], f: block(T)) { + iter2(v) { |_i, v| f(v) } +} + +/* +Function: iter2 + +Iterates over a vector's elements and indexes + +Iterates over vector `v` and, for each element, calls function `f` with the +element's value and index. +*/ +fn iter2<T>(v: [const T], f: block(uint, T)) { + let i = 0u, l = len(v); + while i < l { f(i, v[i]); i += 1u; } +} + +/* +Function: riter + +Iterates over a vector in reverse + +Iterates over vector `v` and, for each element, calls function `f` with the +element's value. + +*/ +fn riter<T>(v: [const T], f: block(T)) { + riter2(v) { |_i, v| f(v) } +} + +/* +Function: riter2 + +Iterates over a vector's elements and indexes in reverse + +Iterates over vector `v` and, for each element, calls function `f` with the +element's value and index. +*/ +fn riter2<T>(v: [const T], f: block(uint, T)) { + let i = len(v); + while 0u < i { + i -= 1u; + f(i, v[i]); + }; +} + +/* +Function: permute + +Iterate over all permutations of vector `v`. Permutations are produced in +lexicographic order with respect to the order of elements in `v` (so if `v` +is sorted then the permutations are lexicographically sorted). + +The total number of permutations produced is `len(v)!`. If `v` contains +repeated elements, then some permutations are repeated. +*/ +fn permute<copy T>(v: [const T], put: block([T])) { + let ln = len(v); + if ln == 0u { + put([]); + } else { + let i = 0u; + while i < ln { + let elt = v[i]; + let rest = slice(v, 0u, i) + slice(v, i+1u, ln); + permute(rest) {|permutation| put([elt] + permutation)} + i += 1u; + } + } +} + +/* +Function: to_ptr + +FIXME: We don't need this wrapper +*/ +unsafe fn to_ptr<T>(v: [T]) -> *T { ret unsafe::to_ptr(v); } + +/* +Module: unsafe +*/ +mod unsafe { + type vec_repr = {mutable fill: uint, mutable alloc: uint, data: u8}; + + /* + Function: from_buf + + Constructs a vector from an unsafe pointer to a buffer + + Parameters: + + ptr - An unsafe pointer to a buffer of `T` + elts - The number of elements in the buffer + */ + unsafe fn from_buf<T>(ptr: *T, elts: uint) -> [T] { + ret rustrt::vec_from_buf_shared(sys::get_type_desc::<T>(), + ptr, elts); + } + + /* + Function: set_len + + Sets the length of a vector + + This well explicitly set the size of the vector, without actually + modifing its buffers, so it is up to the caller to ensure that + the vector is actually the specified size. + */ + unsafe fn set_len<T>(&v: [const T], new_len: uint) { + let repr: **vec_repr = ::unsafe::reinterpret_cast(addr_of(v)); + (**repr).fill = new_len * sys::size_of::<T>(); + } + + /* + Function: to_ptr + + Returns an unsafe pointer to the vector's buffer + + The caller must ensure that the vector outlives the pointer this + function returns, or else it will end up pointing to garbage. + + Modifying the vector may cause its buffer to be reallocated, which + would also make any pointers to it invalid. + */ + unsafe fn to_ptr<T>(v: [const T]) -> *T { + let repr: **vec_repr = ::unsafe::reinterpret_cast(addr_of(v)); + ret ::unsafe::reinterpret_cast(addr_of((**repr).data)); + } +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/win32_fs.rs b/src/libstd/win32_fs.rs new file mode 100644 index 00000000000..87c97fe35c9 --- /dev/null +++ b/src/libstd/win32_fs.rs @@ -0,0 +1,35 @@ + + +#[abi = "cdecl"] +native mod rustrt { + fn rust_list_files(path: str) -> [str]; +} + +fn list_dir(path: str) -> [str] { + let path = path + "*"; + ret rustrt::rust_list_files(path); +} + +fn path_is_absolute(p: str) -> bool { + ret str::char_at(p, 0u) == '/' || + str::char_at(p, 1u) == ':' + && (str::char_at(p, 2u) == path_sep + || str::char_at(p, 2u) == alt_path_sep); +} + +/* FIXME: win32 path handling actually accepts '/' or '\' and has subtly + * different semantics for each. Since we build on mingw, we are usually + * dealing with /-separated paths. But the whole interface to splitting and + * joining pathnames needs a bit more abstraction on win32. Possibly a vec or + * tag type. + */ +const path_sep: char = '/'; + +const alt_path_sep: char = '\\'; +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/win32_os.rs b/src/libstd/win32_os.rs new file mode 100644 index 00000000000..9f648ac9721 --- /dev/null +++ b/src/libstd/win32_os.rs @@ -0,0 +1,133 @@ +import ctypes::*; + +#[abi = "cdecl"] +#[link_name = ""] +native mod libc { + fn read(fd: fd_t, buf: *u8, count: size_t) -> ssize_t; + fn write(fd: fd_t, buf: *u8, count: size_t) -> ssize_t; + fn fread(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t; + fn fwrite(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t; + #[link_name = "_open"] + fn open(s: str::sbuf, flags: c_int, mode: unsigned) -> c_int; + #[link_name = "_close"] + fn close(fd: fd_t) -> c_int; + type FILE; + fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; + fn _fdopen(fd: fd_t, mode: str::sbuf) -> FILE; + fn fclose(f: FILE); + fn fflush(f: FILE) -> c_int; + fn fileno(f: FILE) -> fd_t; + fn fgetc(f: FILE) -> c_int; + fn ungetc(c: c_int, f: FILE); + fn feof(f: FILE) -> c_int; + fn fseek(f: FILE, offset: long, whence: c_int) -> c_int; + fn ftell(f: FILE) -> long; + fn _pipe(fds: *mutable fd_t, size: unsigned, mode: c_int) -> c_int; +} + +mod libc_constants { + const O_RDONLY: c_int = 0i32; + const O_WRONLY: c_int = 1i32; + const O_RDWR: c_int = 2i32; + const O_APPEND: c_int = 8i32; + const O_CREAT: c_int = 256i32; + const O_EXCL: c_int = 1024i32; + const O_TRUNC: c_int = 512i32; + const O_TEXT: c_int = 16384i32; + const O_BINARY: c_int = 32768i32; + const O_NOINHERIT: c_int = 128i32; + const S_IRUSR: unsigned = 256u32; // really _S_IREAD in win32 + const S_IWUSR: unsigned = 128u32; // really _S_IWRITE in win32 +} + +type DWORD = u32; +type HMODULE = uint; +type LPTSTR = str::sbuf; +type LPCTSTR = str::sbuf; + +#[abi = "stdcall"] +native mod kernel32 { + type LPSECURITY_ATTRIBUTES; + fn GetEnvironmentVariableA(n: str::sbuf, v: str::sbuf, nsize: uint) -> + uint; + fn SetEnvironmentVariableA(n: str::sbuf, v: str::sbuf) -> int; + fn GetModuleFileNameA(hModule: HMODULE, + lpFilename: LPTSTR, + nSize: DWORD) -> DWORD; + fn CreateDirectoryA(lpPathName: LPCTSTR, + lpSecurityAttributes: LPSECURITY_ATTRIBUTES) -> bool; + fn RemoveDirectoryA(lpPathName: LPCTSTR) -> bool; + fn SetCurrentDirectoryA(lpPathName: LPCTSTR) -> bool; +} + +// FIXME turn into constants +fn exec_suffix() -> str { ret ".exe"; } +fn target_os() -> str { ret "win32"; } + +fn dylib_filename(base: str) -> str { ret base + ".dll"; } + +fn pipe() -> {in: fd_t, out: fd_t} { + // Windows pipes work subtly differently than unix pipes, and their + // inheritance has to be handled in a different way that I don't fully + // understand. Here we explicitly make the pipe non-inheritable, + // which means to pass it to a subprocess they need to be duplicated + // first, as in rust_run_program. + let fds = {mutable in: 0i32, mutable out: 0i32}; + let res = + os::libc::_pipe(ptr::mut_addr_of(fds.in), 1024u32, + libc_constants::O_BINARY | + libc_constants::O_NOINHERIT); + assert (res == 0i32); + assert (fds.in != -1i32 && fds.in != 0i32); + assert (fds.out != -1i32 && fds.in != 0i32); + ret {in: fds.in, out: fds.out}; +} + +fn fd_FILE(fd: fd_t) -> libc::FILE { + ret str::as_buf("r", {|modebuf| libc::_fdopen(fd, modebuf) }); +} + +fn close(fd: fd_t) -> c_int { + libc::close(fd) +} + +fn fclose(file: libc::FILE) { + libc::fclose(file) +} + +fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { + // FIXME (1253) + fail; +} + +#[abi = "cdecl"] +native mod rustrt { + fn rust_process_wait(handle: c_int) -> c_int; + fn rust_getcwd() -> str; +} + +fn waitpid(pid: pid_t) -> i32 { ret rustrt::rust_process_wait(pid); } + +fn getcwd() -> str { ret rustrt::rust_getcwd(); } + +fn get_exe_path() -> option::t<fs::path> { + // FIXME: This doesn't handle the case where the buffer is too small + let bufsize = 1023u; + let path = str::unsafe_from_bytes(vec::init_elt(0u8, bufsize)); + ret str::as_buf(path, { |path_buf| + if kernel32::GetModuleFileNameA(0u, path_buf, + bufsize as u32) != 0u32 { + option::some(fs::dirname(path) + fs::path_sep()) + } else { + option::none + } + }); +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: |
