<commit_before>macro_rules! endpoint {
    // No reexports besides `Request` and `Response`.
    ($(#[$attr:meta])+ [$($outer_mod:ident),*], $inner_mod:ident) => {
        endpoint!($(#[$attr])+ [$($outer_mod),*], $inner_mod, []);
    };

    // No imports from super.
    ($(#[$attr:meta])+ [$($outer_mod:ident),*], $inner_mod:ident, [$($import:ident),*]) => {
        endpoint!($(#[$attr])+ [$($outer_mod),*], $inner_mod, [], []);
    };

    // Explicit case.
    (
        $(#[$attr:meta])+
        [$($outer_mod:ident),*],
        $inner_mod:ident,
        [$($import:ident),*],
        [$($super_import:ident),*]
    ) => {
        #[$($attr)+]
        pub mod $inner_mod {
            use futures::Future;
            use hyper::client::Connect;
            use ruma_client_api::$($outer_mod::)*$inner_mod::Endpoint;
            $(use super::$super_import;)*
            pub use ruma_client_api::$($outer_mod::)*$inner_mod::{
                Request,
                Response,
                $($import),*
            };

            use {Client, Error};

            /// Make a request to this API endpoint.
            pub fn call<'a, C>(
                client: &'a Client<C>,
                request: Request,
            ) -> impl Future<Item = Response, Error = Error> + 'a
            where
                C: Connect,
            {
                client.request::<Endpoint>(request)
            }
        }
    };
}

/// Endpoints for the r0.x.x versions of the client API specification.
pub mod r0 {
    /// Account registration and management.
    pub mod account {
        endpoint!(
            /// Change the password for an account on this homeserver.
            [r0, account],
            change_password
        );

        endpoint!(
            /// Deactivate the user's account, removing all ability for the user to log in again.
            [r0, account],
            deactivate
        );

        endpoint!(
            /// Register for an account on this homeserver.
            [r0, account],
            register,
            [AuthenticationData, RegistrationKind]
        );

        endpoint!(
            /// Request a password change token by email.
            [r0, account],
            request_password_change_token
        );

        endpoint!(
            /// Request an account registration token by email.
            [r0, account],
            request_register_token
        );
    }

    /// Room aliases.
    pub mod alias {
        endpoint!(
            /// Create a new mapping from a room alias to a room ID.
            [r0, alias],
            create_alias
        );

        endpoint!(
            /// Remove a mapping from a room alias to a room ID.
            [r0, alias],
            delete_alias
        );

        endpoint!(
            /// Resolve a room alias to the corresponding room ID.
            [r0, alias],
            get_alias
        );
    }

    /// Client configuration.
    pub mod config {
        endpoint!(
            /// Set account data for the user.
            [r0, config],
            set_global_account_data
        );

        endpoint!(
            /// Set account data scoped to a room for the user.
            [r0, config],
            set_room_account_data
        );
    }

    /// Account contact information.
    pub mod contact {
        endpoint!(
            /// Add contact information to the user's account.
            [r0, contact],
            create_contact,
            [ThreePidCredentials]
        );

        endpoint!(
            /// Get a list of the third party identifiers that the homeserver has associated with the user's account.
            [r0, contact],
            get_contacts,
            [Medium, ThirdPartyIdentifier]
        );

        endpoint!(
            /// Request an email address verification token by email.
            [r0, contact],
            request_contact_verification_token
        );
    }

    /// Event context.
    pub mod context {
        endpoint!(
            /// Get a number of events that happened just before and after a given event.
            [r0, context],
            get_context
        );
    }

    /// The public room directory.
    pub mod directory {
        endpoint!(
            /// Get a number of events that happened just before and after a given event.
            [r0, directory],
            get_public_rooms,
            [PublicRoomsChunk]
        );
    }

    /// Event filters.
    pub mod filter {
        pub use ruma_client_api::r0::filter::{
            EventFormat,
            Filter,
            FilterDefinition,
            RoomEventFilter,
            RoomFilter,
        };

        endpoint!(
            /// Create a new filter.
            [r0, filter],
            create_filter
        );

        endpoint!(
            /// Get a filter.
            [r0, filter],
            get_filter
        );
    }

    /// Media repository.
    pub mod media {
        endpoint!(
            /// Upload media to the media repository.
            [r0, media],
            create_content
        );

        endpoint!(
            /// Download media from the media repository.
            [r0, media],
            get_content
        );

        endpoint!(
            /// Download a thumbnail image  for the media in the media repository.
            [r0, media],
            get_content_thumbnail,
            [Method]
        );
    }

    /// Room membership.
    pub mod membership {
        pub use ruma_client_api::r0::membership::ThirdPartySigned;

        endpoint!(
            /// Ban a user from a room.
            [r0, membership],
            ban_user
        );

        endpoint!(
            /// Permanently forget a room.
            [r0, membership],
            forget_room
        );

        endpoint!(
            /// Invite a user to a room.
            [r0, membership],
            invite_user
        );

        endpoint!(
            /// Join a room using its ID.
            [r0, membership],
            join_room_by_id
        );

        endpoint!(
            /// Join a room using its ID or an alias.
            [r0, membership],
            join_room_by_id_or_alias
        );

        endpoint!(
            /// Kick a user from a room.
            [r0, membership],
            kick_user
        );

        endpoint!(
            /// Leave a room.
            [r0, membership],
            leave_room
        );

        endpoint!(
            /// Unban a user from a room.
            [r0, membership],
            unban_user
        );
    }

    /// User presence.
    pub mod presence {
        endpoint!(
            /// Get a user's presence state.
            [r0, presence],
            get_presence
        );

        endpoint!(
            /// Get a list of presence events for users on the presence subscription list.
            [r0, presence],
            get_subscribed_presences
        );

        endpoint!(
            /// Set a user's presence state.
            [r0, presence],
            set_presence
        );

        endpoint!(
            /// Add or remove users from the presence subscription list.
            [r0, presence],
            update_presence_subscriptions
        );
    }

    /// User profiles.
    pub mod profile {
        endpoint!(
            /// Get the URL for a user's avatar.
            [r0, profile],
            get_avatar_url
        );

        endpoint!(
            /// Get a user's display name.
            [r0, profile],
            get_display_name
        );

        endpoint!(
            /// Get a user's full profile.
            [r0, profile],
            get_profile
        );

        endpoint!(
            /// Set the URL to the user's avatar.
            [r0, profile],
            set_avatar_url
        );

        endpoint!(
            /// Set the user's display name.
            [r0, profile],
            set_display_name
        );
    }

    /// Push notifications.
    pub mod push {
    }

    /// Event receipts.
    pub mod receipt {
        endpoint!(
            /// Update a receipt marker to point to a given event.
            [r0, receipt],
            create_receipt,
            [ReceiptType]
        );
    }

    /// Event redaction.
    pub mod redact {
        endpoint!(
            /// Redact an event from a room.
            [r0, redact],
            redact_event
        );
    }

    /// Room creation.
    pub mod room {
        endpoint!(
            /// Create a room.
            [r0, room],
            create_room,
            [CreationContent, RoomPreset, Visibility]
        );
    }

    /// Event searches.
    pub mod search {
        endpoint!(
            /// Search for events.
            [r0, search],
            search_events,
            [
                Categories,
                Criteria,
                EventContext,
                EventContextResult,
                Grouping,
                Groupings,
                ResultCategories,
                ResultGroup,
                RoomEventResults,
                SearchResult,
                UserProfile,
                GroupingKey,
                OrderBy,
                SearchKeys
            ]
        );
    }

    /// Sending events.
    pub mod send {
        endpoint!(
            /// Send a message to a room.
            [r0, send],
            send_message_event
        );

        endpoint!(
            /// Send a state event with an empty state key.
            [r0, send],
            send_state_event_for_empty_key
        );

        endpoint!(
            /// Send a state event with a particular state key.
            [r0, send],
            send_state_event_for_key
        );
    }

    /// Server administration.
    pub mod server {
        endpoint!(
            /// Get administrative information about a user.
            [r0, server],
            get_user_info,
            [ConnectionInfo, DeviceInfo, SessionInfo]
        );
    }

    /// User session management.
    pub mod session {
        endpoint!(
            /// Log in to an account, creating an access token.
            [r0, session],
            login,
            [LoginType, Medium]
        );

        endpoint!(
            /// Log out of an account by invalidating the access token.
            [r0, session],
            logout
        );
    }

    /// Getting and synchronizing events.
    pub mod sync {
        endpoint!(
            /// Get the list of members for a room.
            [r0, sync],
            get_member_events
        );

        endpoint!(
            /// Get message and state events for a room.
            [r0, sync],
            get_message_events,
            [Direction]
        );

        endpoint!(
            /// Get the state events for the current state of a room.
            [r0, sync],
            get_state_events
        );

        endpoint!(
            /// Get a particular state event with an empty state key for a room.
            [r0, sync],
            get_state_events_for_empty_key
        );

        endpoint!(
            /// Get a particular state event with a particular state key for a room.
            [r0, sync],
            get_state_events_for_key
        );

        endpoint!(
            /// Synchronize the client's state with the latest state on the homeserver.
            [r0, sync],
            sync_events,
            [
                AccountData,
                Ephemeral,
                Filter,
                InviteState,
                InvitedRoom,
                JoinedRoom,
                LeftRoom,
                Presence,
                Rooms,
                SetPresence,
                State,
                Timeline,
                UnreadNotificationsCount
            ]
        );
    }

    /// Tagging rooms.
    pub mod tag {
        endpoint!(
            /// Create a tag on a room.
            [r0, tag],
            create_tag
        );

        endpoint!(
            /// Delete a tag on a room.
            [r0, tag],
            delete_tag
        );

        endpoint!(
            /// Get the user's tags for a room.
            [r0, tag],
            get_tags
        );
    }

    /// Typing notifications.
    pub mod typing {
        endpoint!(
            /// Indicate that the user is currently typing.
            [r0, typing],
            create_typing_event
        );
    }

    /// Voice over IP.
    pub mod voip {
        endpoint!(
            /// Get credentials for initiating voice over IP calls via a TURN server.
            [r0, voip],
            get_turn_server_info
        );
    }
}

/// Endpoints that cannot change with new versions of the Matrix specification.
pub mod unversioned {
    endpoint!(
        /// Get the versions of the specification supported by this homeserver.
        [unversioned],
        get_supported_versions
    );
}
<commit_msg>Pass local imports to the more explicit version of the endpoint macro.<commit_after>macro_rules! endpoint {
    // No reexports besides `Request` and `Response`.
    ($(#[$attr:meta])+ [$($outer_mod:ident),*], $inner_mod:ident) => {
        endpoint!($(#[$attr])+ [$($outer_mod),*], $inner_mod, []);
    };

    // No imports from super.
    ($(#[$attr:meta])+ [$($outer_mod:ident),*], $inner_mod:ident, [$($import:ident),*]) => {
        endpoint!($(#[$attr])+ [$($outer_mod),*], $inner_mod, [$($import),*], []);
    };

    // Explicit case.
    (
        $(#[$attr:meta])+
        [$($outer_mod:ident),*],
        $inner_mod:ident,
        [$($import:ident),*],
        [$($super_import:ident),*]
    ) => {
        #[$($attr)+]
        pub mod $inner_mod {
            use futures::Future;
            use hyper::client::Connect;
            use ruma_client_api::$($outer_mod::)*$inner_mod::Endpoint;
            $(use super::$super_import;)*
            pub use ruma_client_api::$($outer_mod::)*$inner_mod::{
                Request,
                Response,
                $($import),*
            };

            use {Client, Error};

            /// Make a request to this API endpoint.
            pub fn call<'a, C>(
                client: &'a Client<C>,
                request: Request,
            ) -> impl Future<Item = Response, Error = Error> + 'a
            where
                C: Connect,
            {
                client.request::<Endpoint>(request)
            }
        }
    };
}

/// Endpoints for the r0.x.x versions of the client API specification.
pub mod r0 {
    /// Account registration and management.
    pub mod account {
        endpoint!(
            /// Change the password for an account on this homeserver.
            [r0, account],
            change_password
        );

        endpoint!(
            /// Deactivate the user's account, removing all ability for the user to log in again.
            [r0, account],
            deactivate
        );

        endpoint!(
            /// Register for an account on this homeserver.
            [r0, account],
            register,
            [AuthenticationData, RegistrationKind]
        );

        endpoint!(
            /// Request a password change token by email.
            [r0, account],
            request_password_change_token
        );

        endpoint!(
            /// Request an account registration token by email.
            [r0, account],
            request_register_token
        );
    }

    /// Room aliases.
    pub mod alias {
        endpoint!(
            /// Create a new mapping from a room alias to a room ID.
            [r0, alias],
            create_alias
        );

        endpoint!(
            /// Remove a mapping from a room alias to a room ID.
            [r0, alias],
            delete_alias
        );

        endpoint!(
            /// Resolve a room alias to the corresponding room ID.
            [r0, alias],
            get_alias
        );
    }

    /// Client configuration.
    pub mod config {
        endpoint!(
            /// Set account data for the user.
            [r0, config],
            set_global_account_data
        );

        endpoint!(
            /// Set account data scoped to a room for the user.
            [r0, config],
            set_room_account_data
        );
    }

    /// Account contact information.
    pub mod contact {
        endpoint!(
            /// Add contact information to the user's account.
            [r0, contact],
            create_contact,
            [ThreePidCredentials]
        );

        endpoint!(
            /// Get a list of the third party identifiers that the homeserver has associated with the user's account.
            [r0, contact],
            get_contacts,
            [Medium, ThirdPartyIdentifier]
        );

        endpoint!(
            /// Request an email address verification token by email.
            [r0, contact],
            request_contact_verification_token
        );
    }

    /// Event context.
    pub mod context {
        endpoint!(
            /// Get a number of events that happened just before and after a given event.
            [r0, context],
            get_context
        );
    }

    /// The public room directory.
    pub mod directory {
        endpoint!(
            /// Get a number of events that happened just before and after a given event.
            [r0, directory],
            get_public_rooms,
            [PublicRoomsChunk]
        );
    }

    /// Event filters.
    pub mod filter {
        pub use ruma_client_api::r0::filter::{
            EventFormat,
            Filter,
            FilterDefinition,
            RoomEventFilter,
            RoomFilter,
        };

        endpoint!(
            /// Create a new filter.
            [r0, filter],
            create_filter
        );

        endpoint!(
            /// Get a filter.
            [r0, filter],
            get_filter
        );
    }

    /// Media repository.
    pub mod media {
        endpoint!(
            /// Upload media to the media repository.
            [r0, media],
            create_content
        );

        endpoint!(
            /// Download media from the media repository.
            [r0, media],
            get_content
        );

        endpoint!(
            /// Download a thumbnail image  for the media in the media repository.
            [r0, media],
            get_content_thumbnail,
            [Method]
        );
    }

    /// Room membership.
    pub mod membership {
        pub use ruma_client_api::r0::membership::ThirdPartySigned;

        endpoint!(
            /// Ban a user from a room.
            [r0, membership],
            ban_user
        );

        endpoint!(
            /// Permanently forget a room.
            [r0, membership],
            forget_room
        );

        endpoint!(
            /// Invite a user to a room.
            [r0, membership],
            invite_user
        );

        endpoint!(
            /// Join a room using its ID.
            [r0, membership],
            join_room_by_id
        );

        endpoint!(
            /// Join a room using its ID or an alias.
            [r0, membership],
            join_room_by_id_or_alias
        );

        endpoint!(
            /// Kick a user from a room.
            [r0, membership],
            kick_user
        );

        endpoint!(
            /// Leave a room.
            [r0, membership],
            leave_room
        );

        endpoint!(
            /// Unban a user from a room.
            [r0, membership],
            unban_user
        );
    }

    /// User presence.
    pub mod presence {
        endpoint!(
            /// Get a user's presence state.
            [r0, presence],
            get_presence
        );

        endpoint!(
            /// Get a list of presence events for users on the presence subscription list.
            [r0, presence],
            get_subscribed_presences
        );

        endpoint!(
            /// Set a user's presence state.
            [r0, presence],
            set_presence
        );

        endpoint!(
            /// Add or remove users from the presence subscription list.
            [r0, presence],
            update_presence_subscriptions
        );
    }

    /// User profiles.
    pub mod profile {
        endpoint!(
            /// Get the URL for a user's avatar.
            [r0, profile],
            get_avatar_url
        );

        endpoint!(
            /// Get a user's display name.
            [r0, profile],
            get_display_name
        );

        endpoint!(
            /// Get a user's full profile.
            [r0, profile],
            get_profile
        );

        endpoint!(
            /// Set the URL to the user's avatar.
            [r0, profile],
            set_avatar_url
        );

        endpoint!(
            /// Set the user's display name.
            [r0, profile],
            set_display_name
        );
    }

    /// Push notifications.
    pub mod push {
    }

    /// Event receipts.
    pub mod receipt {
        endpoint!(
            /// Update a receipt marker to point to a given event.
            [r0, receipt],
            create_receipt,
            [ReceiptType]
        );
    }

    /// Event redaction.
    pub mod redact {
        endpoint!(
            /// Redact an event from a room.
            [r0, redact],
            redact_event
        );
    }

    /// Room creation.
    pub mod room {
        endpoint!(
            /// Create a room.
            [r0, room],
            create_room,
            [CreationContent, RoomPreset, Visibility]
        );
    }

    /// Event searches.
    pub mod search {
        endpoint!(
            /// Search for events.
            [r0, search],
            search_events,
            [
                Categories,
                Criteria,
                EventContext,
                EventContextResult,
                Grouping,
                Groupings,
                ResultCategories,
                ResultGroup,
                RoomEventResults,
                SearchResult,
                UserProfile,
                GroupingKey,
                OrderBy,
                SearchKeys
            ]
        );
    }

    /// Sending events.
    pub mod send {
        endpoint!(
            /// Send a message to a room.
            [r0, send],
            send_message_event
        );

        endpoint!(
            /// Send a state event with an empty state key.
            [r0, send],
            send_state_event_for_empty_key
        );

        endpoint!(
            /// Send a state event with a particular state key.
            [r0, send],
            send_state_event_for_key
        );
    }

    /// Server administration.
    pub mod server {
        endpoint!(
            /// Get administrative information about a user.
            [r0, server],
            get_user_info,
            [ConnectionInfo, DeviceInfo, SessionInfo]
        );
    }

    /// User session management.
    pub mod session {
        endpoint!(
            /// Log in to an account, creating an access token.
            [r0, session],
            login,
            [LoginType, Medium]
        );

        endpoint!(
            /// Log out of an account by invalidating the access token.
            [r0, session],
            logout
        );
    }

    /// Getting and synchronizing events.
    pub mod sync {
        endpoint!(
            /// Get the list of members for a room.
            [r0, sync],
            get_member_events
        );

        endpoint!(
            /// Get message and state events for a room.
            [r0, sync],
            get_message_events,
            [Direction]
        );

        endpoint!(
            /// Get the state events for the current state of a room.
            [r0, sync],
            get_state_events
        );

        endpoint!(
            /// Get a particular state event with an empty state key for a room.
            [r0, sync],
            get_state_events_for_empty_key
        );

        endpoint!(
            /// Get a particular state event with a particular state key for a room.
            [r0, sync],
            get_state_events_for_key
        );

        endpoint!(
            /// Synchronize the client's state with the latest state on the homeserver.
            [r0, sync],
            sync_events,
            [
                AccountData,
                Ephemeral,
                Filter,
                InviteState,
                InvitedRoom,
                JoinedRoom,
                LeftRoom,
                Presence,
                Rooms,
                SetPresence,
                State,
                Timeline,
                UnreadNotificationsCount
            ]
        );
    }

    /// Tagging rooms.
    pub mod tag {
        endpoint!(
            /// Create a tag on a room.
            [r0, tag],
            create_tag
        );

        endpoint!(
            /// Delete a tag on a room.
            [r0, tag],
            delete_tag
        );

        endpoint!(
            /// Get the user's tags for a room.
            [r0, tag],
            get_tags
        );
    }

    /// Typing notifications.
    pub mod typing {
        endpoint!(
            /// Indicate that the user is currently typing.
            [r0, typing],
            create_typing_event
        );
    }

    /// Voice over IP.
    pub mod voip {
        endpoint!(
            /// Get credentials for initiating voice over IP calls via a TURN server.
            [r0, voip],
            get_turn_server_info
        );
    }
}

/// Endpoints that cannot change with new versions of the Matrix specification.
pub mod unversioned {
    endpoint!(
        /// Get the versions of the specification supported by this homeserver.
        [unversioned],
        get_supported_versions
    );
}
<|endoftext|>


<commit_before>pub enum GTPError {
    NotImplemented,
    InvalidBoardSize,
    InvalidMove,
    BadVertexList,
    BoardNotEmpty,
    CannotUndo,
}

pub enum Colour {
    Black,
    White
}

#[allow(dead_code)]
pub struct Vertex {
    x: u8, // letter
    y: u8  // number
}

pub enum Move {
    Stone(Vertex),
    Pass,
    Resign
}

#[allow(dead_code)]
pub struct ColouredMove {
    player: Colour,
    move: Move
}

pub enum StoneStatus {
    Alive,
    Seki,
    Dead
}

pub trait GoBot {
    // Static functions identifying the bot :

    // name (ex : "My super Bot")
    fn gtp_name() -> &'static str;
    // version (ex : "v2.3-r5")
    fn gtp_version() -> &'static str;

    // Any function returning a GTPError that it is not supposed
    // to return will be fatal to the framework.

    // Basic functions, must be implemented

    // clear_board : clears the board, can never fail
    fn gtp_clear_board(&mut self) -> ();
    // komi : sets the komi, can never fail, must accept absurd values
    fn gtp_komi(&mut self, komi: f32) -> ();
    // boardsize : sets the board size.
    // Returns InvalidBoardSize if the size is not supported.
    fn gtp_boardsize(&mut self, size: uint) -> Result<(), GTPError>;
    // play : plays the provided move on the board
    // Returns InvalidMove is the move is invalid
    fn gtp_play(&mut self, move: ColouredMove) -> Result<(), GTPError>;
    // genmove : ask the bot for a move of the chosen color
    // cannot fail, the bot must provide a move even if the last
    // played move is of the same colour
    // plays the move as well
    fn gtp_genmove(&mut self, player: Colour) -> Move;

    // Optional functions, if not iplemented, the corresponding
    // commands will not be activated
    // All these functions will be called once by the framework
    // at startup, then clear_board will be called

    // genmove_regression : like genmove, but must be deterministic
    // and must not actually play the move
    // should always return Ok(Move), never raise any error
    #[allow(unused_variable)]
    fn gtp_genmove_regression(&self, player: Colour) -> Result<Move, GTPError> {
        Err(NotImplemented)
    }
    // undo : undo last move if possible
    // if not, return Err(CannotUndo)
    // if undo is never possible, should not be implemented
    #[allow(unused_variable)]
    fn gtp_undo(&mut self) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // place_free_handicap : The bot places its handicap stones
    // and returns a slice to a vector of Vertexes
    // it can place less stones if the asked number is too high
    // fails with Err(BoardNotEmpty) if board isn't empty
    #[allow(unused_variable)]
    fn gtp_place_free_handicap(&mut self, number: uint) -> Result<&[Vertex], GTPError> {
        Err(NotImplemented)
    }
    // set_free_handicap : uses the provided list as handicap stones
    // for black
    // fails with Err(BoardNotEmpty) if board isn't empty
    // fails woth Err(BadVertexList) if the vertex list is unusable
    // (two stones at the same place, or stones outside the board)
    #[allow(unused_variable)]
    fn gtp_set_free_handicap(&mut self, stones: &[Vertex]) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // time_settings : sets the time settings for the game
    // it is only informative, the btot should count it's own time,
    // but the controller is supposed to enforce it
    // time are give in minute, should never fail
    #[allow(unused_variable)]
    fn gtp_time_settings(&mut self, main_time: int, byoyomi_time: int, byoyomi_stones: int) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // final_status_list : returns a slice to the list of stones of
    // any color in the given status, in the opinion of the bot
    // should never fail
    #[allow(unused_variable)]
    fn gtp_final_status_list(&self, status: StoneStatus) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // final_score : computes the bot's calculation of the final score
    // if it is a draw, float value must be 0 and colour is not important
    // should never fail
    #[allow(unused_variable)]
    fn gtp_final_score(&self) -> Result<(f32, Colour), GTPError> {
        Err(NotImplemented)
    }
    // showboard : returns a description of the board as saw by the bot :
    // (boardsize, black_stones, white_stones, black_captured_count, white_captured_count)
    // should never fail
    #[allow(unused_variable)]
    fn gtp_showboard(&self) -> Result<(int, &[Vertex], &[Vertex], int, int), GTPError> {
        Err(NotImplemented)
    }
}

// Vertex implementation for messing with strings
impl Vertex {
    #[allow(dead_code)]
    pub fn from_coords(x: u8, y:u8) -> Vertex {
        if x == 0 || x > 25 || y == 0 || y > 25 {
            fail!("Vertex coordinates must be in 1-25.");
        }
        Vertex{x: x, y: y}
    }

    #[allow(dead_code)]
    pub fn from_str(text: &str) -> Vertex {
        if text.len() < 2 || text.len() > 3 {
            fail!("Invalid text coordinates.");
        }
        let mut x: u8 = text.as_bytes()[0];
        if x < ('A' as u8) || x > ('Z' as u8) || (x as char) == 'I' {
            fail!("Invalid text coordinates.");
        }
        x -= ('A' as u8) - 1;
        if x > 9 {
            x -= 1;
        } // eliminate 'I'
        let number = ::std::u8::parse_bytes(text.as_bytes().slice_from(1), 10);
        let mut y: u8 = 0;
        match number {
            Some(num) => y = num,
            _ => (),
        }
        if y == 0 || y > 25 {
            fail!("Invalid text coordinates.");
        }
        Vertex{x: x, y: y}
    }

    #[allow(dead_code)]
    pub fn to_coords(&self) -> (u8, u8) {
        (self.x, self.y)
    }

    #[allow(dead_code)]
    pub fn to_string(&self) -> String {
        let mut letter: u8 = 'A' as u8;
        if self.x >= 9 {
            // eliminate 'I'
            letter += self.x;
        } else {
            letter += self.x-1;
        }
        format!("{:c}{:u}", letter as char, self.y)
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn vertex_to_string() {
        let vrtx1 = super::Vertex::from_coords(8u8, 7u8);
        assert_eq!(vrtx1.to_string().as_slice(), "H7");
        let vrtx2 = super::Vertex::from_coords(9u8, 13u8);
        assert_eq!(vrtx2.to_string().as_slice(), "J13");
        let vrtx3 = super::Vertex::from_coords(19u8, 1u8);
        assert_eq!(vrtx3.to_string().as_slice(), "T1");
    }

    #[test]
    fn string_to_vertex() {
        let vrtx1 = super::Vertex::from_str("C7");
        assert_eq!(vrtx1.to_coords(), (3u8, 7u8));
        let vrtx2 = super::Vertex::from_str("J11");
        assert_eq!(vrtx2.to_coords(), (9u8, 11u8));
        let vrtx3 = super::Vertex::from_str("Z25");
        assert_eq!(vrtx3.to_coords(), (25u8, 25u8));
    }

    #[test]
    #[should_fail]
    fn too_big_coordinates() {
        let vrtx = super::Vertex::from_coords(26u8, 13u8);
        assert_eq!(vrtx.to_coords(), (26u8, 13u8));
    }

    #[test]
    #[should_fail]
    fn invalid_string() {
        let vrtx = super::Vertex::from_str("I13");
        assert_eq!(vrtx.to_coords(), (9u8, 13u8));
    }

}
<commit_msg>Use cleaner error handling in API.<commit_after>pub enum GTPError {
    NotImplemented,
    InvalidBoardSize,
    InvalidMove,
    BadVertexList,
    BoardNotEmpty,
    CannotUndo,
}

pub enum Colour {
    Black,
    White
}

#[allow(dead_code)]
pub struct Vertex {
    x: u8, // letter
    y: u8  // number
}

pub enum Move {
    Stone(Vertex),
    Pass,
    Resign
}

#[allow(dead_code)]
pub struct ColouredMove {
    player: Colour,
    move: Move
}

pub enum StoneStatus {
    Alive,
    Seki,
    Dead
}

pub trait GoBot {
    // Static functions identifying the bot :

    // name (ex : "My super Bot")
    fn gtp_name() -> &'static str;
    // version (ex : "v2.3-r5")
    fn gtp_version() -> &'static str;

    // Any function returning a GTPError that it is not supposed
    // to return will be fatal to the framework.

    // Basic functions, must be implemented

    // clear_board : clears the board, can never fail
    fn gtp_clear_board(&mut self) -> ();
    // komi : sets the komi, can never fail, must accept absurd values
    fn gtp_komi(&mut self, komi: f32) -> ();
    // boardsize : sets the board size.
    // Returns InvalidBoardSize if the size is not supported.
    fn gtp_boardsize(&mut self, size: uint) -> Result<(), GTPError>;
    // play : plays the provided move on the board
    // Returns InvalidMove is the move is invalid
    fn gtp_play(&mut self, move: ColouredMove) -> Result<(), GTPError>;
    // genmove : ask the bot for a move of the chosen color
    // cannot fail, the bot must provide a move even if the last
    // played move is of the same colour
    // plays the move as well
    fn gtp_genmove(&mut self, player: Colour) -> Move;

    // Optional functions, if not iplemented, the corresponding
    // commands will not be activated
    // All these functions will be called once by the framework
    // at startup, then clear_board will be called

    // genmove_regression : like genmove, but must be deterministic
    // and must not actually play the move
    // should always return Ok(Move), never raise any error
    #[allow(unused_variable)]
    fn gtp_genmove_regression(&self, player: Colour) -> Result<Move, GTPError> {
        Err(NotImplemented)
    }
    // undo : undo last move if possible
    // if not, return Err(CannotUndo)
    // if undo is never possible, should not be implemented
    #[allow(unused_variable)]
    fn gtp_undo(&mut self) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // place_free_handicap : The bot places its handicap stones
    // and returns a slice to a vector of Vertexes
    // it can place less stones if the asked number is too high
    // fails with Err(BoardNotEmpty) if board isn't empty
    #[allow(unused_variable)]
    fn gtp_place_free_handicap(&mut self, number: uint) -> Result<&[Vertex], GTPError> {
        Err(NotImplemented)
    }
    // set_free_handicap : uses the provided list as handicap stones
    // for black
    // fails with Err(BoardNotEmpty) if board isn't empty
    // fails woth Err(BadVertexList) if the vertex list is unusable
    // (two stones at the same place, or stones outside the board)
    #[allow(unused_variable)]
    fn gtp_set_free_handicap(&mut self, stones: &[Vertex]) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // time_settings : sets the time settings for the game
    // it is only informative, the btot should count it's own time,
    // but the controller is supposed to enforce it
    // time are give in minute, should never fail
    #[allow(unused_variable)]
    fn gtp_time_settings(&mut self, main_time: int, byoyomi_time: int, byoyomi_stones: int) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // final_status_list : returns a slice to the list of stones of
    // any color in the given status, in the opinion of the bot
    // should never fail
    #[allow(unused_variable)]
    fn gtp_final_status_list(&self, status: StoneStatus) -> Result<(), GTPError> {
        Err(NotImplemented)
    }
    // final_score : computes the bot's calculation of the final score
    // if it is a draw, float value must be 0 and colour is not important
    // should never fail
    #[allow(unused_variable)]
    fn gtp_final_score(&self) -> Result<(f32, Colour), GTPError> {
        Err(NotImplemented)
    }
    // showboard : returns a description of the board as saw by the bot :
    // (boardsize, black_stones, white_stones, black_captured_count, white_captured_count)
    // should never fail
    #[allow(unused_variable)]
    fn gtp_showboard(&self) -> Result<(int, &[Vertex], &[Vertex], int, int), GTPError> {
        Err(NotImplemented)
    }
}

// Vertex implementation for messing with strings
impl Vertex {
    #[allow(dead_code)]
    pub fn from_coords(x: u8, y:u8) -> Option<Vertex> {
        if x == 0 || x > 25 || y == 0 || y > 25 {
            None
        } else {
            Some(Vertex{x: x, y: y})
        }
    }

    #[allow(dead_code)]
    pub fn from_str(text: &str) -> Option<Vertex> {
        if text.len() < 2 || text.len() > 3 {
            return None;
        }
        let mut x: u8 = text.as_bytes()[0];
        if x < ('A' as u8) || x > ('Z' as u8) || (x as char) == 'I' {
            return None;
        }
        x -= ('A' as u8) - 1;
        if x > 9 {
            x -= 1;
        } // eliminate 'I'
        let number = ::std::u8::parse_bytes(text.as_bytes().slice_from(1), 10);
        let mut y: u8 = 0;
        match number {
            Some(num) => y = num,
            _ => (),
        }
        if y == 0 || y > 25 {
            return None;
        }
        Some(Vertex{x: x, y: y})
    }

    #[allow(dead_code)]
    pub fn to_coords(&self) -> (u8, u8) {
        (self.x, self.y)
    }

    #[allow(dead_code)]
    pub fn to_string(&self) -> String {
        let mut letter: u8 = 'A' as u8;
        if self.x >= 9 {
            // eliminate 'I'
            letter += self.x;
        } else {
            letter += self.x-1;
        }
        format!("{:c}{:u}", letter as char, self.y)
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn vertex_to_string() {
        let vrtx1 = super::Vertex::from_coords(8u8, 7u8).unwrap();
        assert_eq!(vrtx1.to_string().as_slice(), "H7");
        let vrtx2 = super::Vertex::from_coords(9u8, 13u8).unwrap();
        assert_eq!(vrtx2.to_string().as_slice(), "J13");
        let vrtx3 = super::Vertex::from_coords(19u8, 1u8).unwrap();
        assert_eq!(vrtx3.to_string().as_slice(), "T1");
    }

    #[test]
    fn string_to_vertex() {
        let vrtx1 = super::Vertex::from_str("C7").unwrap();
        assert_eq!(vrtx1.to_coords(), (3u8, 7u8));
        let vrtx2 = super::Vertex::from_str("J11").unwrap();
        assert_eq!(vrtx2.to_coords(), (9u8, 11u8));
        let vrtx3 = super::Vertex::from_str("Z25").unwrap();
        assert_eq!(vrtx3.to_coords(), (25u8, 25u8));
    }

    #[test]
    #[should_fail]
    fn too_big_coordinates() {
        let vrtx = super::Vertex::from_coords(26u8, 13u8).unwrap();
        assert_eq!(vrtx.to_coords(), (26u8, 13u8));
    }

    #[test]
    #[should_fail]
    fn invalid_string() {
        let vrtx = super::Vertex::from_str("I13").unwrap();
        assert_eq!(vrtx.to_coords(), (9u8, 13u8));
    }

}
<|endoftext|>


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

use core::ptr::PtrExt;

// FIXME: #13996: mark the `allocate` and `reallocate` return value as `noalias`

/// Return a pointer to `size` bytes of memory aligned to `align`.
///
/// On failure, return a null pointer.
///
/// Behavior is undefined if the requested size is 0 or the alignment is not a
/// power of 2. The alignment must be no larger than the largest supported page
/// size on the platform.
#[inline]
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
    imp::allocate(size, align)
}

/// Resize the allocation referenced by `ptr` to `size` bytes.
///
/// On failure, return a null pointer and leave the original allocation intact.
///
/// Behavior is undefined if the requested size is 0 or the alignment is not a
/// power of 2. The alignment must be no larger than the largest supported page
/// size on the platform.
///
/// The `old_size` and `align` parameters are the parameters that were used to
/// create the allocation referenced by `ptr`. The `old_size` parameter may be
/// any value in range_inclusive(requested_size, usable_size).
#[inline]
pub unsafe fn reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8 {
    imp::reallocate(ptr, old_size, size, align)
}

/// Resize the allocation referenced by `ptr` to `size` bytes.
///
/// If the operation succeeds, it returns `usable_size(size, align)` and if it
/// fails (or is a no-op) it returns `usable_size(old_size, align)`.
///
/// Behavior is undefined if the requested size is 0 or the alignment is not a
/// power of 2. The alignment must be no larger than the largest supported page
/// size on the platform.
///
/// The `old_size` and `align` parameters are the parameters that were used to
/// create the allocation referenced by `ptr`. The `old_size` parameter may be
/// any value in range_inclusive(requested_size, usable_size).
#[inline]
pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> uint {
    imp::reallocate_inplace(ptr, old_size, size, align)
}

/// Deallocates the memory referenced by `ptr`.
///
/// The `ptr` parameter must not be null.
///
/// The `old_size` and `align` parameters are the parameters that were used to
/// create the allocation referenced by `ptr`. The `old_size` parameter may be
/// any value in range_inclusive(requested_size, usable_size).
#[inline]
pub unsafe fn deallocate(ptr: *mut u8, old_size: uint, align: uint) {
    imp::deallocate(ptr, old_size, align)
}

/// Returns the usable size of an allocation created with the specified the
/// `size` and `align`.
#[inline]
pub fn usable_size(size: uint, align: uint) -> uint {
    imp::usable_size(size, align)
}

/// Prints implementation-defined allocator statistics.
///
/// These statistics may be inconsistent if other threads use the allocator
/// during the call.
#[unstable]
pub fn stats_print() {
    imp::stats_print();
}

/// An arbitrary non-null address to represent zero-size allocations.
///
/// This preserves the non-null invariant for types like `Box<T>`. The address may overlap with
/// non-zero-size memory allocations.
pub const EMPTY: *mut () = 0x1 as *mut ();

/// The allocator for unique pointers.
#[cfg(not(test))]
#[lang="exchange_malloc"]
#[inline]
unsafe fn exchange_malloc(size: uint, align: uint) -> *mut u8 {
    if size == 0 {
        EMPTY as *mut u8
    } else {
        let ptr = allocate(size, align);
        if ptr.is_null() { ::oom() }
        ptr
    }
}

#[cfg(not(test))]
#[lang="exchange_free"]
#[inline]
unsafe fn exchange_free(ptr: *mut u8, old_size: uint, align: uint) {
    deallocate(ptr, old_size, align);
}

// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values. In practice, the alignment is a
// constant at the call site and the branch will be optimized out.
#[cfg(any(target_arch = "arm",
          target_arch = "mips",
          target_arch = "mipsel"))]
const MIN_ALIGN: uint = 8;
#[cfg(any(target_arch = "x86",
          target_arch = "x86_64",
          target_arch = "aarch64"))]
const MIN_ALIGN: uint = 16;

#[cfg(feature = "external_funcs")]
mod imp {
    extern {
        fn rust_allocate(size: uint, align: uint) -> *mut u8;
        fn rust_deallocate(ptr: *mut u8, old_size: uint, align: uint);
        fn rust_reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8;
        fn rust_reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint,
                                   align: uint) -> uint;
        fn rust_usable_size(size: uint, align: uint) -> uint;
        fn rust_stats_print();
    }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        rust_allocate(size, align)
    }

    #[inline]
    pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint,
                                     align: uint) -> uint {
        rust_reallocate_inplace(ptr, old_size, size, align)
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, old_size: uint, align: uint) {
        rust_deallocate(ptr, old_size, align)
    }

    #[inline]
    pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint,
                                     align: uint) -> uint {
        rust_reallocate_inplace(ptr, old_size, size, align)
    }

    #[inline]
    pub fn usable_size(size: uint, align: uint) -> uint {
        unsafe { rust_usable_size(size, align) }
    }

    #[inline]
    pub fn stats_print() {
        unsafe { rust_stats_print() }
    }
}

#[cfg(feature = "external_crate")]
mod imp {
    extern crate external;
    pub use self::external::{allocate, deallocate, reallocate_inplace, reallocate};
    pub use self::external::{usable_size, stats_print};
}

#[cfg(all(not(feature = "external_funcs"),
          not(feature = "external_crate"),
          jemalloc))]
mod imp {
    use core::option::Option;
    use core::option::Option::None;
    use core::ptr::{null_mut, null};
    use core::num::Int;
    use libc::{c_char, c_int, c_void, size_t};
    use super::MIN_ALIGN;

    #[link(name = "jemalloc", kind = "static")]
    #[cfg(not(test))]
    extern {}

    extern {
        fn je_mallocx(size: size_t, flags: c_int) -> *mut c_void;
        fn je_rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
        fn je_xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t;
        fn je_sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
        fn je_nallocx(size: size_t, flags: c_int) -> size_t;
        fn je_malloc_stats_print(write_cb: Option<extern "C" fn(cbopaque: *mut c_void,
                                                                *const c_char)>,
                                 cbopaque: *mut c_void,
                                 opts: *const c_char);
    }

    // -lpthread needs to occur after -ljemalloc, the earlier argument isn't enough
    #[cfg(all(not(windows), not(target_os = "android")))]
    #[link(name = "pthread")]
    extern {}

    // MALLOCX_ALIGN(a) macro
    #[inline(always)]
    fn mallocx_align(a: uint) -> c_int { a.trailing_zeros() as c_int }

    #[inline(always)]
    fn align_to_flags(align: uint) -> c_int {
        if align <= MIN_ALIGN { 0 } else { mallocx_align(align) }
    }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        let flags = align_to_flags(align);
        je_mallocx(size as size_t, flags) as *mut u8
    }

    #[inline]
    pub unsafe fn reallocate(ptr: *mut u8, _old_size: uint, size: uint, align: uint) -> *mut u8 {
        let flags = align_to_flags(align);
        je_rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8
    }

    #[inline]
    pub unsafe fn reallocate_inplace(ptr: *mut u8, _old_size: uint, size: uint,
                                     align: uint) -> uint {
        let flags = align_to_flags(align);
        je_xallocx(ptr as *mut c_void, size as size_t, 0, flags) as uint
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, old_size: uint, align: uint) {
        let flags = align_to_flags(align);
        je_sdallocx(ptr as *mut c_void, old_size as size_t, flags)
    }

    #[inline]
    pub fn usable_size(size: uint, align: uint) -> uint {
        let flags = align_to_flags(align);
        unsafe { je_nallocx(size as size_t, flags) as uint }
    }

    pub fn stats_print() {
        unsafe {
            je_malloc_stats_print(None, null_mut(), null())
        }
    }
}

#[cfg(all(not(feature = "external_funcs"),
          not(feature = "external_crate"),
          not(jemalloc),
          unix))]
mod imp {
    use core::cmp;
    use core::ptr;
    use libc;
    use super::MIN_ALIGN;

    extern {
        fn posix_memalign(memptr: *mut *mut libc::c_void,
                          align: libc::size_t,
                          size: libc::size_t) -> libc::c_int;
    }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        if align <= MIN_ALIGN {
            libc::malloc(size as libc::size_t) as *mut u8
        } else {
            let mut out = 0 as *mut libc::c_void;
            let ret = posix_memalign(&mut out,
                                     align as libc::size_t,
                                     size as libc::size_t);
            if ret != 0 {
                ptr::null_mut()
            } else {
                out as *mut u8
            }
        }
    }

    #[inline]
    pub unsafe fn reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8 {
        if align <= MIN_ALIGN {
            libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8
        } else {
            let new_ptr = allocate(size, align);
            ptr::copy_memory(new_ptr, ptr as *const u8, cmp::min(size, old_size));
            deallocate(ptr, old_size, align);
            new_ptr
        }
    }

    #[inline]
    pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: uint, _size: uint,
                                     _align: uint) -> uint {
        old_size
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, _old_size: uint, _align: uint) {
        libc::free(ptr as *mut libc::c_void)
    }

    #[inline]
    pub fn usable_size(size: uint, _align: uint) -> uint {
        size
    }

    pub fn stats_print() {}
}

#[cfg(all(not(feature = "external_funcs"),
          not(feature = "external_crate"),
          not(jemalloc),
          windows))]
mod imp {
    use libc::{c_void, size_t};
    use libc;
    use super::MIN_ALIGN;

    extern {
        fn _aligned_malloc(size: size_t, align: size_t) -> *mut c_void;
        fn _aligned_realloc(block: *mut c_void, size: size_t,
                            align: size_t) -> *mut c_void;
        fn _aligned_free(ptr: *mut c_void);
    }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        if align <= MIN_ALIGN {
            libc::malloc(size as size_t) as *mut u8
        } else {
            _aligned_malloc(size as size_t, align as size_t) as *mut u8
        }
    }

    #[inline]
    pub unsafe fn reallocate(ptr: *mut u8, _old_size: uint, size: uint, align: uint) -> *mut u8 {
        if align <= MIN_ALIGN {
            libc::realloc(ptr as *mut c_void, size as size_t) as *mut u8
        } else {
            _aligned_realloc(ptr as *mut c_void, size as size_t, align as size_t) as *mut u8
        }
    }

    #[inline]
    pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: uint, _size: uint,
                                     _align: uint) -> uint {
        old_size
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, _old_size: uint, align: uint) {
        if align <= MIN_ALIGN {
            libc::free(ptr as *mut libc::c_void)
        } else {
            _aligned_free(ptr as *mut c_void)
        }
    }

    #[inline]
    pub fn usable_size(size: uint, _align: uint) -> uint {
        size
    }

    pub fn stats_print() {}
}

#[cfg(test)]
mod test {
    extern crate test;
    use self::test::Bencher;
    use core::ptr::PtrExt;
    use heap;

    #[test]
    fn basic_reallocate_inplace_noop() {
        unsafe {
            let size = 4000;
            let ptr = heap::allocate(size, 8);
            if ptr.is_null() { ::oom() }
            let ret = heap::reallocate_inplace(ptr, size, size, 8);
            heap::deallocate(ptr, size, 8);
            assert_eq!(ret, heap::usable_size(size, 8));
        }
    }

    #[bench]
    fn alloc_owned_small(b: &mut Bencher) {
        b.iter(|| {
            box 10i
        })
    }
}
<commit_msg>liballoc's "extern_funcs" impl mod had a duplicate and missing item<commit_after>// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::ptr::PtrExt;

// FIXME: #13996: mark the `allocate` and `reallocate` return value as `noalias`

/// Return a pointer to `size` bytes of memory aligned to `align`.
///
/// On failure, return a null pointer.
///
/// Behavior is undefined if the requested size is 0 or the alignment is not a
/// power of 2. The alignment must be no larger than the largest supported page
/// size on the platform.
#[inline]
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
    imp::allocate(size, align)
}

/// Resize the allocation referenced by `ptr` to `size` bytes.
///
/// On failure, return a null pointer and leave the original allocation intact.
///
/// Behavior is undefined if the requested size is 0 or the alignment is not a
/// power of 2. The alignment must be no larger than the largest supported page
/// size on the platform.
///
/// The `old_size` and `align` parameters are the parameters that were used to
/// create the allocation referenced by `ptr`. The `old_size` parameter may be
/// any value in range_inclusive(requested_size, usable_size).
#[inline]
pub unsafe fn reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8 {
    imp::reallocate(ptr, old_size, size, align)
}

/// Resize the allocation referenced by `ptr` to `size` bytes.
///
/// If the operation succeeds, it returns `usable_size(size, align)` and if it
/// fails (or is a no-op) it returns `usable_size(old_size, align)`.
///
/// Behavior is undefined if the requested size is 0 or the alignment is not a
/// power of 2. The alignment must be no larger than the largest supported page
/// size on the platform.
///
/// The `old_size` and `align` parameters are the parameters that were used to
/// create the allocation referenced by `ptr`. The `old_size` parameter may be
/// any value in range_inclusive(requested_size, usable_size).
#[inline]
pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> uint {
    imp::reallocate_inplace(ptr, old_size, size, align)
}

/// Deallocates the memory referenced by `ptr`.
///
/// The `ptr` parameter must not be null.
///
/// The `old_size` and `align` parameters are the parameters that were used to
/// create the allocation referenced by `ptr`. The `old_size` parameter may be
/// any value in range_inclusive(requested_size, usable_size).
#[inline]
pub unsafe fn deallocate(ptr: *mut u8, old_size: uint, align: uint) {
    imp::deallocate(ptr, old_size, align)
}

/// Returns the usable size of an allocation created with the specified the
/// `size` and `align`.
#[inline]
pub fn usable_size(size: uint, align: uint) -> uint {
    imp::usable_size(size, align)
}

/// Prints implementation-defined allocator statistics.
///
/// These statistics may be inconsistent if other threads use the allocator
/// during the call.
#[unstable]
pub fn stats_print() {
    imp::stats_print();
}

/// An arbitrary non-null address to represent zero-size allocations.
///
/// This preserves the non-null invariant for types like `Box<T>`. The address may overlap with
/// non-zero-size memory allocations.
pub const EMPTY: *mut () = 0x1 as *mut ();

/// The allocator for unique pointers.
#[cfg(not(test))]
#[lang="exchange_malloc"]
#[inline]
unsafe fn exchange_malloc(size: uint, align: uint) -> *mut u8 {
    if size == 0 {
        EMPTY as *mut u8
    } else {
        let ptr = allocate(size, align);
        if ptr.is_null() { ::oom() }
        ptr
    }
}

#[cfg(not(test))]
#[lang="exchange_free"]
#[inline]
unsafe fn exchange_free(ptr: *mut u8, old_size: uint, align: uint) {
    deallocate(ptr, old_size, align);
}

// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values. In practice, the alignment is a
// constant at the call site and the branch will be optimized out.
#[cfg(any(target_arch = "arm",
          target_arch = "mips",
          target_arch = "mipsel"))]
const MIN_ALIGN: uint = 8;
#[cfg(any(target_arch = "x86",
          target_arch = "x86_64",
          target_arch = "aarch64"))]
const MIN_ALIGN: uint = 16;

#[cfg(feature = "external_funcs")]
mod imp {
    extern {
        fn rust_allocate(size: uint, align: uint) -> *mut u8;
        fn rust_deallocate(ptr: *mut u8, old_size: uint, align: uint);
        fn rust_reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8;
        fn rust_reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint,
                                   align: uint) -> uint;
        fn rust_usable_size(size: uint, align: uint) -> uint;
        fn rust_stats_print();
    }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        rust_allocate(size, align)
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, old_size: uint, align: uint) {
        rust_deallocate(ptr, old_size, align)
    }

    #[inline]
    pub unsafe fn reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8 {
        rust_reallocate(ptr, old_size, size, align)
    }

    #[inline]
    pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint,
                                     align: uint) -> uint {
        rust_reallocate_inplace(ptr, old_size, size, align)
    }

    #[inline]
    pub fn usable_size(size: uint, align: uint) -> uint {
        unsafe { rust_usable_size(size, align) }
    }

    #[inline]
    pub fn stats_print() {
        unsafe { rust_stats_print() }
    }
}

#[cfg(feature = "external_crate")]
mod imp {
    extern crate external;
    pub use self::external::{allocate, deallocate, reallocate_inplace, reallocate};
    pub use self::external::{usable_size, stats_print};
}

#[cfg(all(not(feature = "external_funcs"),
          not(feature = "external_crate"),
          jemalloc))]
mod imp {
    use core::option::Option;
    use core::option::Option::None;
    use core::ptr::{null_mut, null};
    use core::num::Int;
    use libc::{c_char, c_int, c_void, size_t};
    use super::MIN_ALIGN;

    #[link(name = "jemalloc", kind = "static")]
    #[cfg(not(test))]
    extern {}

    extern {
        fn je_mallocx(size: size_t, flags: c_int) -> *mut c_void;
        fn je_rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
        fn je_xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t;
        fn je_sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
        fn je_nallocx(size: size_t, flags: c_int) -> size_t;
        fn je_malloc_stats_print(write_cb: Option<extern "C" fn(cbopaque: *mut c_void,
                                                                *const c_char)>,
                                 cbopaque: *mut c_void,
                                 opts: *const c_char);
    }

    // -lpthread needs to occur after -ljemalloc, the earlier argument isn't enough
    #[cfg(all(not(windows), not(target_os = "android")))]
    #[link(name = "pthread")]
    extern {}

    // MALLOCX_ALIGN(a) macro
    #[inline(always)]
    fn mallocx_align(a: uint) -> c_int { a.trailing_zeros() as c_int }

    #[inline(always)]
    fn align_to_flags(align: uint) -> c_int {
        if align <= MIN_ALIGN { 0 } else { mallocx_align(align) }
    }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        let flags = align_to_flags(align);
        je_mallocx(size as size_t, flags) as *mut u8
    }

    #[inline]
    pub unsafe fn reallocate(ptr: *mut u8, _old_size: uint, size: uint, align: uint) -> *mut u8 {
        let flags = align_to_flags(align);
        je_rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8
    }

    #[inline]
    pub unsafe fn reallocate_inplace(ptr: *mut u8, _old_size: uint, size: uint,
                                     align: uint) -> uint {
        let flags = align_to_flags(align);
        je_xallocx(ptr as *mut c_void, size as size_t, 0, flags) as uint
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, old_size: uint, align: uint) {
        let flags = align_to_flags(align);
        je_sdallocx(ptr as *mut c_void, old_size as size_t, flags)
    }

    #[inline]
    pub fn usable_size(size: uint, align: uint) -> uint {
        let flags = align_to_flags(align);
        unsafe { je_nallocx(size as size_t, flags) as uint }
    }

    pub fn stats_print() {
        unsafe {
            je_malloc_stats_print(None, null_mut(), null())
        }
    }
}

#[cfg(all(not(feature = "external_funcs"),
          not(feature = "external_crate"),
          not(jemalloc),
          unix))]
mod imp {
    use core::cmp;
    use core::ptr;
    use libc;
    use super::MIN_ALIGN;

    extern {
        fn posix_memalign(memptr: *mut *mut libc::c_void,
                          align: libc::size_t,
                          size: libc::size_t) -> libc::c_int;
    }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        if align <= MIN_ALIGN {
            libc::malloc(size as libc::size_t) as *mut u8
        } else {
            let mut out = 0 as *mut libc::c_void;
            let ret = posix_memalign(&mut out,
                                     align as libc::size_t,
                                     size as libc::size_t);
            if ret != 0 {
                ptr::null_mut()
            } else {
                out as *mut u8
            }
        }
    }

    #[inline]
    pub unsafe fn reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8 {
        if align <= MIN_ALIGN {
            libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8
        } else {
            let new_ptr = allocate(size, align);
            ptr::copy_memory(new_ptr, ptr as *const u8, cmp::min(size, old_size));
            deallocate(ptr, old_size, align);
            new_ptr
        }
    }

    #[inline]
    pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: uint, _size: uint,
                                     _align: uint) -> uint {
        old_size
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, _old_size: uint, _align: uint) {
        libc::free(ptr as *mut libc::c_void)
    }

    #[inline]
    pub fn usable_size(size: uint, _align: uint) -> uint {
        size
    }

    pub fn stats_print() {}
}

#[cfg(all(not(feature = "external_funcs"),
          not(feature = "external_crate"),
          not(jemalloc),
          windows))]
mod imp {
    use libc::{c_void, size_t};
    use libc;
    use super::MIN_ALIGN;

    extern {
        fn _aligned_malloc(size: size_t, align: size_t) -> *mut c_void;
        fn _aligned_realloc(block: *mut c_void, size: size_t,
                            align: size_t) -> *mut c_void;
        fn _aligned_free(ptr: *mut c_void);
    }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        if align <= MIN_ALIGN {
            libc::malloc(size as size_t) as *mut u8
        } else {
            _aligned_malloc(size as size_t, align as size_t) as *mut u8
        }
    }

    #[inline]
    pub unsafe fn reallocate(ptr: *mut u8, _old_size: uint, size: uint, align: uint) -> *mut u8 {
        if align <= MIN_ALIGN {
            libc::realloc(ptr as *mut c_void, size as size_t) as *mut u8
        } else {
            _aligned_realloc(ptr as *mut c_void, size as size_t, align as size_t) as *mut u8
        }
    }

    #[inline]
    pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: uint, _size: uint,
                                     _align: uint) -> uint {
        old_size
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, _old_size: uint, align: uint) {
        if align <= MIN_ALIGN {
            libc::free(ptr as *mut libc::c_void)
        } else {
            _aligned_free(ptr as *mut c_void)
        }
    }

    #[inline]
    pub fn usable_size(size: uint, _align: uint) -> uint {
        size
    }

    pub fn stats_print() {}
}

#[cfg(test)]
mod test {
    extern crate test;
    use self::test::Bencher;
    use core::ptr::PtrExt;
    use heap;

    #[test]
    fn basic_reallocate_inplace_noop() {
        unsafe {
            let size = 4000;
            let ptr = heap::allocate(size, 8);
            if ptr.is_null() { ::oom() }
            let ret = heap::reallocate_inplace(ptr, size, size, 8);
            heap::deallocate(ptr, size, 8);
            assert_eq!(ret, heap::usable_size(size, 8));
        }
    }

    #[bench]
    fn alloc_owned_small(b: &mut Bencher) {
        b.iter(|| {
            box 10i
        })
    }
}
<|endoftext|>


<commit_before>#![crate_name = "uu_cat"]

// This file is part of the uutils coreutils package.
//
// (c) Jordi Boggiano <j.boggiano@seld.be>
// (c) Evgeniy Klyuchikov <evgeniy.klyuchikov@gmail.com>
// (c) Joshua S. Miller <jsmiller@uchicago.edu>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

#[macro_use]
extern crate quick_error;
#[cfg(unix)]
extern crate unix_socket;
#[macro_use]
extern crate uucore;

// last synced with: cat (GNU coreutils) 8.13
use quick_error::ResultExt;
use std::fs::{metadata, File};
use std::io::{self, stderr, stdin, stdout, BufWriter, Read, Write};
use uucore::fs::is_stdin_interactive;

/// Unix domain socket support
#[cfg(unix)]
use std::net::Shutdown;
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
#[cfg(unix)]
use unix_socket::UnixStream;

static SYNTAX: &str = "[OPTION]... [FILE]...";
static SUMMARY: &str = "Concatenate FILE(s), or standard input, to standard output
 With no FILE, or when FILE is -, read standard input.";
static LONG_HELP: &str = "";

#[derive(PartialEq)]
enum NumberingMode {
    NumberNone,
    NumberNonEmpty,
    NumberAll,
}

quick_error! {
    #[derive(Debug)]
    enum CatError {
        /// Wrapper for io::Error with path context
        Input(err: io::Error, path: String) {
            display("cat: {0}: {1}", path, err)
            context(path: &'a str, err: io::Error) -> (err, path.to_owned())
            cause(err)
        }

        /// Wrapper for io::Error with no context
        Output(err: io::Error) {
            display("cat: {0}", err) from()
            cause(err)
        }

        /// Uknown Filetype  classification
        UnknownFiletype(path: String) {
            display("cat: {0}: unknown filetype", path)
        }

        /// At least one error was encountered in reading or writing
        EncounteredErrors(count: usize) {
            display("cat: encountered {0} errors", count)
        }

        /// Denotes an error caused by trying to `cat` a directory
        IsDirectory(path: String) {
            display("cat: {0}: Is a directory", path)
        }
    }
}

struct OutputOptions {
    /// Line numbering mode
    number: NumberingMode,

    /// Suppress repeated empty output lines
    squeeze_blank: bool,

    /// display TAB characters as `tab`
    show_tabs: bool,

    /// If `show_tabs == true`, this string will be printed in the
    /// place of tabs
    tab: String,

    /// Can be set to show characters other than '\n' a the end of
    /// each line, e.g. $
    end_of_line: String,

    /// use ^ and M- notation, except for LF (\\n) and TAB (\\t)
    show_nonprint: bool,
}

/// Represents an open file handle, stream, or other device
struct InputHandle {
    reader: Box<dyn Read>,
    is_interactive: bool,
}

/// Concrete enum of recognized file types.
///
/// *Note*: `cat`-ing a directory should result in an
/// CatError::IsDirectory
enum InputType {
    Directory,
    File,
    StdIn,
    SymLink,
    #[cfg(unix)]
    BlockDevice,
    #[cfg(unix)]
    CharacterDevice,
    #[cfg(unix)]
    Fifo,
    #[cfg(unix)]
    Socket,
}

type CatResult<T> = Result<T, CatError>;

pub fn uumain(args: Vec<String>) -> i32 {
    let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
        .optflag("A", "show-all", "equivalent to -vET")
        .optflag(
            "b",
            "number-nonblank",
            "number nonempty output lines, overrides -n",
        )
        .optflag("e", "", "equivalent to -vE")
        .optflag("E", "show-ends", "display $ at end of each line")
        .optflag("n", "number", "number all output lines")
        .optflag("s", "squeeze-blank", "suppress repeated empty output lines")
        .optflag("t", "", "equivalent to -vT")
        .optflag("T", "show-tabs", "display TAB characters as ^I")
        .optflag(
            "v",
            "show-nonprinting",
            "use ^ and M- notation, except for LF (\\n) and TAB (\\t)",
        )
        .parse(args);

    let number_mode = if matches.opt_present("b") {
        NumberingMode::NumberNonEmpty
    } else if matches.opt_present("n") {
        NumberingMode::NumberAll
    } else {
        NumberingMode::NumberNone
    };

    let show_nonprint = matches.opts_present(&[
        "A".to_owned(),
        "e".to_owned(),
        "t".to_owned(),
        "v".to_owned(),
    ]);
    let show_ends = matches.opts_present(&["E".to_owned(), "A".to_owned(), "e".to_owned()]);
    let show_tabs = matches.opts_present(&["A".to_owned(), "T".to_owned(), "t".to_owned()]);
    let squeeze_blank = matches.opt_present("s");
    let mut files = matches.free;
    if files.is_empty() {
        files.push("-".to_owned());
    }

    let can_write_fast = !(show_tabs || show_nonprint || show_ends || squeeze_blank
        || number_mode != NumberingMode::NumberNone);

    let success = if can_write_fast {
        write_fast(files).is_ok()
    } else {
        let tab = if show_tabs { "^I" } else { "\t" }.to_owned();

        let end_of_line = if show_ends { "$\n" } else { "\n" }.to_owned();

        let options = OutputOptions {
            end_of_line,
            number: number_mode,
            show_nonprint,
            show_tabs,
            squeeze_blank,
            tab,
        };

        write_lines(files, &options).is_ok()
    };

    if success { 0 } else { 1 }
}

/// Classifies the `InputType` of file at `path` if possible
///
/// # Arguments
///
/// * `path` - Path on a file system to classify metadata
fn get_input_type(path: &str) -> CatResult<InputType> {
    if path == "-" {
        return Ok(InputType::StdIn);
    }

    match metadata(path).context(path)?.file_type() {
        #[cfg(unix)]
        ft if ft.is_block_device() =>
        {
            Ok(InputType::BlockDevice)
        }
        #[cfg(unix)]
        ft if ft.is_char_device() =>
        {
            Ok(InputType::CharacterDevice)
        }
        #[cfg(unix)]
        ft if ft.is_fifo() =>
        {
            Ok(InputType::Fifo)
        }
        #[cfg(unix)]
        ft if ft.is_socket() =>
        {
            Ok(InputType::Socket)
        }
        ft if ft.is_dir() => Ok(InputType::Directory),
        ft if ft.is_file() => Ok(InputType::File),
        ft if ft.is_symlink() => Ok(InputType::SymLink),
        _ => Err(CatError::UnknownFiletype(path.to_owned())),
    }
}

/// Returns an InputHandle from which a Reader can be accessed or an
/// error
///
/// # Arguments
///
/// * `path` - `InputHandler` will wrap a reader from this file path
fn open(path: &str) -> CatResult<InputHandle> {
    if path == "-" {
        let stdin = stdin();
        return Ok(InputHandle {
            reader: Box::new(stdin) as Box<dyn Read>,
            is_interactive: is_stdin_interactive(),
        });
    }

    match get_input_type(path)? {
        InputType::Directory => Err(CatError::IsDirectory(path.to_owned())),
        #[cfg(unix)]
        InputType::Socket => {
            let socket = UnixStream::connect(path).context(path)?;
            socket.shutdown(Shutdown::Write).context(path)?;
            Ok(InputHandle {
                reader: Box::new(socket) as Box<dyn Read>,
                is_interactive: false,
            })
        }
        _ => {
            let file = File::open(path).context(path)?;
            Ok(InputHandle {
                reader: Box::new(file) as Box<dyn Read>,
                is_interactive: false,
            })
        }
    }
}

/// Writes files to stdout with no configuration.  This allows a
/// simple memory copy. Returns `Ok(())` if no errors were
/// encountered, or an error with the number of errors encountered.
///
/// # Arguments
///
/// * `files` - There is no short circuit when encountiner an error
/// reading a file in this vector
fn write_fast(files: Vec<String>) -> CatResult<()> {
    let mut writer = stdout();
    let mut in_buf = [0; 1024 * 64];
    let mut error_count = 0;

    for file in files {
        match open(&file[..]) {
            Ok(mut handle) => while let Ok(n) = handle.reader.read(&mut in_buf) {
                if n == 0 {
                    break;
                }
                writer.write_all(&in_buf[..n]).context(&file[..])?;
            },
            Err(error) => {
                writeln!(&mut stderr(), "{}", error)?;
                error_count += 1;
            }
        }
    }

    match error_count {
        0 => Ok(()),
        _ => Err(CatError::EncounteredErrors(error_count)),
    }
}

/// State that persists between output of each file
struct OutputState {
    /// The current line number
    line_number: usize,

    /// Whether the output cursor is at the beginning of a new line
    at_line_start: bool,
}

/// Writes files to stdout with `options` as configuration.  Returns
/// `Ok(())` if no errors were encountered, or an error with the
/// number of errors encountered.
///
/// # Arguments
///
/// * `files` - There is no short circuit when encountiner an error
/// reading a file in this vector
fn write_lines(files: Vec<String>, options: &OutputOptions) -> CatResult<()> {
    let mut error_count = 0;
    let mut state = OutputState {
        line_number: 1,
        at_line_start: true,
    };

    for file in files {
        if let Err(error) = write_file_lines(&file, options, &mut state) {
            writeln!(&mut stderr(), "{}", error).context(&file[..])?;
            error_count += 1;
        }
    }

    match error_count {
        0 => Ok(()),
        _ => Err(CatError::EncounteredErrors(error_count)),
    }
}

/// Outputs file contents to stdout in a linewise fashion,
/// propagating any errors that might occur.
fn write_file_lines(file: &str, options: &OutputOptions, state: &mut OutputState) -> CatResult<()> {
    let mut handle = open(file)?;
    let mut in_buf = [0; 1024 * 31];
    let mut writer = BufWriter::with_capacity(1024 * 64, stdout());
    let mut one_blank_kept = false;

    while let Ok(n) = handle.reader.read(&mut in_buf) {
        if n == 0 {
            break;
        }
        let in_buf = &in_buf[..n];
        let mut pos = 0;
        while pos < n {
            // skip empty line_number enumerating them if needed
            if in_buf[pos] == b'\n' {
                if !state.at_line_start || !options.squeeze_blank || !one_blank_kept {
                    one_blank_kept = true;
                    if state.at_line_start && options.number == NumberingMode::NumberAll {
                        write!(&mut writer, "{0:6}\t", state.line_number)?;
                        state.line_number += 1;
                    }
                    writer.write_all(options.end_of_line.as_bytes())?;
                    if handle.is_interactive {
                        writer.flush().context(&file[..])?;
                    }
                }
                state.at_line_start = true;
                pos += 1;
                continue;
            }
            one_blank_kept = false;
            if state.at_line_start && options.number != NumberingMode::NumberNone {
                write!(&mut writer, "{0:6}\t", state.line_number)?;
                state.line_number += 1;
            }

            // print to end of line or end of buffer
            let offset = if options.show_nonprint {
                write_nonprint_to_end(&in_buf[pos..], &mut writer, options.tab.as_bytes())
            } else if options.show_tabs {
                write_tab_to_end(&in_buf[pos..], &mut writer)
            } else {
                write_to_end(&in_buf[pos..], &mut writer)
            };
            // end of buffer?
            if offset == 0 {
                state.at_line_start = false;
                break;
            }
            // print suitable end of line
            writer.write_all(options.end_of_line.as_bytes())?;
            if handle.is_interactive {
                writer.flush()?;
            }
            state.at_line_start = true;
            pos += offset;
        }
    }

    Ok(())
}

// write***_to_end methods
// Write all symbols till end of line or end of buffer is reached
// Return the (number of written symbols + 1) or 0 if the end of buffer is reached
fn write_to_end<W: Write>(in_buf: &[u8], writer: &mut W) -> usize {
    match in_buf.iter().position(|c| *c == b'\n') {
        Some(p) => {
            writer.write_all(&in_buf[..p]).unwrap();
            p + 1
        }
        None => {
            writer.write_all(in_buf).unwrap();
            0
        }
    }
}

fn write_tab_to_end<W: Write>(mut in_buf: &[u8], writer: &mut W) -> usize {
    let mut count = 0;
    loop {
        match in_buf
            .iter()
            .position(|c| *c == b'\n' || *c == b'\t')
        {
            Some(p) => {
                writer.write_all(&in_buf[..p]).unwrap();
                if in_buf[p] == b'\n' {
                    return count + p + 1;
                } else {
                    writer.write_all(b"^I").unwrap();
                    in_buf = &in_buf[p + 1..];
                    count += p + 1;
                }
            }
            None => {
                writer.write_all(in_buf).unwrap();
                return 0;
            }
        };
    }
}

fn write_nonprint_to_end<W: Write>(in_buf: &[u8], writer: &mut W, tab: &[u8]) -> usize {
    let mut count = 0;

    for byte in in_buf.iter().map(|c| *c) {
        if byte == b'\n' {
            break;
        }
        match byte {
            9 => writer.write_all(tab),
            0...8 | 10...31 => writer.write_all(&[b'^', byte + 64]),
            32...126 => writer.write_all(&[byte]),
            127 => writer.write_all(&[b'^', byte - 64]),
            128...159 => writer.write_all(&[b'M', b'-', b'^', byte - 64]),
            160...254 => writer.write_all(&[b'M', b'-', byte - 128]),
            _ => writer.write_all(&[b'M', b'-', b'^', 63]),
        }.unwrap();
        count += 1;
    }
    if count != in_buf.len() {
        count + 1
    } else {
        0
    }
}
<commit_msg>Empty test to start with cat<commit_after>#![crate_name = "uu_cat"]

// This file is part of the uutils coreutils package.
//
// (c) Jordi Boggiano <j.boggiano@seld.be>
// (c) Evgeniy Klyuchikov <evgeniy.klyuchikov@gmail.com>
// (c) Joshua S. Miller <jsmiller@uchicago.edu>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

#[macro_use]
extern crate quick_error;
#[cfg(unix)]
extern crate unix_socket;
#[macro_use]
extern crate uucore;

// last synced with: cat (GNU coreutils) 8.13
use quick_error::ResultExt;
use std::fs::{metadata, File};
use std::io::{self, stderr, stdin, stdout, BufWriter, Read, Write};
use uucore::fs::is_stdin_interactive;

/// Unix domain socket support
#[cfg(unix)]
use std::net::Shutdown;
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
#[cfg(unix)]
use unix_socket::UnixStream;

static SYNTAX: &str = "[OPTION]... [FILE]...";
static SUMMARY: &str = "Concatenate FILE(s), or standard input, to standard output
 With no FILE, or when FILE is -, read standard input.";
static LONG_HELP: &str = "";

#[derive(PartialEq)]
enum NumberingMode {
    NumberNone,
    NumberNonEmpty,
    NumberAll,
}

quick_error! {
    #[derive(Debug)]
    enum CatError {
        /// Wrapper for io::Error with path context
        Input(err: io::Error, path: String) {
            display("cat: {0}: {1}", path, err)
            context(path: &'a str, err: io::Error) -> (err, path.to_owned())
            cause(err)
        }

        /// Wrapper for io::Error with no context
        Output(err: io::Error) {
            display("cat: {0}", err) from()
            cause(err)
        }

        /// Uknown Filetype  classification
        UnknownFiletype(path: String) {
            display("cat: {0}: unknown filetype", path)
        }

        /// At least one error was encountered in reading or writing
        EncounteredErrors(count: usize) {
            display("cat: encountered {0} errors", count)
        }

        /// Denotes an error caused by trying to `cat` a directory
        IsDirectory(path: String) {
            display("cat: {0}: Is a directory", path)
        }
    }
}

struct OutputOptions {
    /// Line numbering mode
    number: NumberingMode,

    /// Suppress repeated empty output lines
    squeeze_blank: bool,

    /// display TAB characters as `tab`
    show_tabs: bool,

    /// If `show_tabs == true`, this string will be printed in the
    /// place of tabs
    tab: String,

    /// Can be set to show characters other than '\n' a the end of
    /// each line, e.g. $
    end_of_line: String,

    /// use ^ and M- notation, except for LF (\\n) and TAB (\\t)
    show_nonprint: bool,
}

/// Represents an open file handle, stream, or other device
struct InputHandle {
    reader: Box<dyn Read>,
    is_interactive: bool,
}

/// Concrete enum of recognized file types.
///
/// *Note*: `cat`-ing a directory should result in an
/// CatError::IsDirectory
enum InputType {
    Directory,
    File,
    StdIn,
    SymLink,
    #[cfg(unix)]
    BlockDevice,
    #[cfg(unix)]
    CharacterDevice,
    #[cfg(unix)]
    Fifo,
    #[cfg(unix)]
    Socket,
}

type CatResult<T> = Result<T, CatError>;

pub fn uumain(args: Vec<String>) -> i32 {
    let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
        .optflag("A", "show-all", "equivalent to -vET")
        .optflag(
            "b",
            "number-nonblank",
            "number nonempty output lines, overrides -n",
        )
        .optflag("e", "", "equivalent to -vE")
        .optflag("E", "show-ends", "display $ at end of each line")
        .optflag("n", "number", "number all output lines")
        .optflag("s", "squeeze-blank", "suppress repeated empty output lines")
        .optflag("t", "", "equivalent to -vT")
        .optflag("T", "show-tabs", "display TAB characters as ^I")
        .optflag(
            "v",
            "show-nonprinting",
            "use ^ and M- notation, except for LF (\\n) and TAB (\\t)",
        )
        .parse(args);

    let number_mode = if matches.opt_present("b") {
        NumberingMode::NumberNonEmpty
    } else if matches.opt_present("n") {
        NumberingMode::NumberAll
    } else {
        NumberingMode::NumberNone
    };

    let show_nonprint = matches.opts_present(&[
        "A".to_owned(),
        "e".to_owned(),
        "t".to_owned(),
        "v".to_owned(),
    ]);
    let show_ends = matches.opts_present(&["E".to_owned(), "A".to_owned(), "e".to_owned()]);
    let show_tabs = matches.opts_present(&["A".to_owned(), "T".to_owned(), "t".to_owned()]);
    let squeeze_blank = matches.opt_present("s");
    let mut files = matches.free;
    if files.is_empty() {
        files.push("-".to_owned());
    }

    let can_write_fast = !(show_tabs || show_nonprint || show_ends || squeeze_blank
        || number_mode != NumberingMode::NumberNone);

    let success = if can_write_fast {
        write_fast(files).is_ok()
    } else {
        let tab = if show_tabs { "^I" } else { "\t" }.to_owned();

        let end_of_line = if show_ends { "$\n" } else { "\n" }.to_owned();

        let options = OutputOptions {
            end_of_line,
            number: number_mode,
            show_nonprint,
            show_tabs,
            squeeze_blank,
            tab,
        };

        write_lines(files, &options).is_ok()
    };

    if success { 0 } else { 1 }
}

/// Classifies the `InputType` of file at `path` if possible
///
/// # Arguments
///
/// * `path` - Path on a file system to classify metadata
fn get_input_type(path: &str) -> CatResult<InputType> {
    if path == "-" {
        return Ok(InputType::StdIn);
    }

    match metadata(path).context(path)?.file_type() {
        #[cfg(unix)]
        ft if ft.is_block_device() =>
        {
            Ok(InputType::BlockDevice)
        }
        #[cfg(unix)]
        ft if ft.is_char_device() =>
        {
            Ok(InputType::CharacterDevice)
        }
        #[cfg(unix)]
        ft if ft.is_fifo() =>
        {
            Ok(InputType::Fifo)
        }
        #[cfg(unix)]
        ft if ft.is_socket() =>
        {
            Ok(InputType::Socket)
        }
        ft if ft.is_dir() => Ok(InputType::Directory),
        ft if ft.is_file() => Ok(InputType::File),
        ft if ft.is_symlink() => Ok(InputType::SymLink),
        _ => Err(CatError::UnknownFiletype(path.to_owned())),
    }
}

/// Returns an InputHandle from which a Reader can be accessed or an
/// error
///
/// # Arguments
///
/// * `path` - `InputHandler` will wrap a reader from this file path
fn open(path: &str) -> CatResult<InputHandle> {
    if path == "-" {
        let stdin = stdin();
        return Ok(InputHandle {
            reader: Box::new(stdin) as Box<dyn Read>,
            is_interactive: is_stdin_interactive(),
        });
    }

    match get_input_type(path)? {
        InputType::Directory => Err(CatError::IsDirectory(path.to_owned())),
        #[cfg(unix)]
        InputType::Socket => {
            let socket = UnixStream::connect(path).context(path)?;
            socket.shutdown(Shutdown::Write).context(path)?;
            Ok(InputHandle {
                reader: Box::new(socket) as Box<dyn Read>,
                is_interactive: false,
            })
        }
        _ => {
            let file = File::open(path).context(path)?;
            Ok(InputHandle {
                reader: Box::new(file) as Box<dyn Read>,
                is_interactive: false,
            })
        }
    }
}

/// Writes files to stdout with no configuration.  This allows a
/// simple memory copy. Returns `Ok(())` if no errors were
/// encountered, or an error with the number of errors encountered.
///
/// # Arguments
///
/// * `files` - There is no short circuit when encountiner an error
/// reading a file in this vector
fn write_fast(files: Vec<String>) -> CatResult<()> {
    let mut writer = stdout();
    let mut in_buf = [0; 1024 * 64];
    let mut error_count = 0;

    for file in files {
        match open(&file[..]) {
            Ok(mut handle) => while let Ok(n) = handle.reader.read(&mut in_buf) {
                if n == 0 {
                    break;
                }
                writer.write_all(&in_buf[..n]).context(&file[..])?;
            },
            Err(error) => {
                writeln!(&mut stderr(), "{}", error)?;
                error_count += 1;
            }
        }
    }

    match error_count {
        0 => Ok(()),
        _ => Err(CatError::EncounteredErrors(error_count)),
    }
}

/// State that persists between output of each file
struct OutputState {
    /// The current line number
    line_number: usize,

    /// Whether the output cursor is at the beginning of a new line
    at_line_start: bool,
}

/// Writes files to stdout with `options` as configuration.  Returns
/// `Ok(())` if no errors were encountered, or an error with the
/// number of errors encountered.
///
/// # Arguments
///
/// * `files` - There is no short circuit when encountiner an error
/// reading a file in this vector
fn write_lines(files: Vec<String>, options: &OutputOptions) -> CatResult<()> {
    let mut error_count = 0;
    let mut state = OutputState {
        line_number: 1,
        at_line_start: true,
    };

    for file in files {
        if let Err(error) = write_file_lines(&file, options, &mut state) {
            writeln!(&mut stderr(), "{}", error).context(&file[..])?;
            error_count += 1;
        }
    }

    match error_count {
        0 => Ok(()),
        _ => Err(CatError::EncounteredErrors(error_count)),
    }
}

/// Outputs file contents to stdout in a linewise fashion,
/// propagating any errors that might occur.
fn write_file_lines(file: &str, options: &OutputOptions, state: &mut OutputState) -> CatResult<()> {
    let mut handle = open(file)?;
    let mut in_buf = [0; 1024 * 31];
    let mut writer = BufWriter::with_capacity(1024 * 64, stdout());
    let mut one_blank_kept = false;

    while let Ok(n) = handle.reader.read(&mut in_buf) {
        if n == 0 {
            break;
        }
        let in_buf = &in_buf[..n];
        let mut pos = 0;
        while pos < n {
            // skip empty line_number enumerating them if needed
            if in_buf[pos] == b'\n' {
                if !state.at_line_start || !options.squeeze_blank || !one_blank_kept {
                    one_blank_kept = true;
                    if state.at_line_start && options.number == NumberingMode::NumberAll {
                        write!(&mut writer, "{0:6}\t", state.line_number)?;
                        state.line_number += 1;
                    }
                    writer.write_all(options.end_of_line.as_bytes())?;
                    if handle.is_interactive {
                        writer.flush().context(&file[..])?;
                    }
                }
                state.at_line_start = true;
                pos += 1;
                continue;
            }
            one_blank_kept = false;
            if state.at_line_start && options.number != NumberingMode::NumberNone {
                write!(&mut writer, "{0:6}\t", state.line_number)?;
                state.line_number += 1;
            }

            // print to end of line or end of buffer
            let offset = if options.show_nonprint {
                write_nonprint_to_end(&in_buf[pos..], &mut writer, options.tab.as_bytes())
            } else if options.show_tabs {
                write_tab_to_end(&in_buf[pos..], &mut writer)
            } else {
                write_to_end(&in_buf[pos..], &mut writer)
            };
            // end of buffer?
            if offset == 0 {
                state.at_line_start = false;
                break;
            }
            // print suitable end of line
            writer.write_all(options.end_of_line.as_bytes())?;
            if handle.is_interactive {
                writer.flush()?;
            }
            state.at_line_start = true;
            pos += offset;
        }
    }

    Ok(())
}

// write***_to_end methods
// Write all symbols till end of line or end of buffer is reached
// Return the (number of written symbols + 1) or 0 if the end of buffer is reached
fn write_to_end<W: Write>(in_buf: &[u8], writer: &mut W) -> usize {
    match in_buf.iter().position(|c| *c == b'\n') {
        Some(p) => {
            writer.write_all(&in_buf[..p]).unwrap();
            p + 1
        }
        None => {
            writer.write_all(in_buf).unwrap();
            0
        }
    }
}

fn write_tab_to_end<W: Write>(mut in_buf: &[u8], writer: &mut W) -> usize {
    let mut count = 0;
    loop {
        match in_buf
            .iter()
            .position(|c| *c == b'\n' || *c == b'\t')
        {
            Some(p) => {
                writer.write_all(&in_buf[..p]).unwrap();
                if in_buf[p] == b'\n' {
                    return count + p + 1;
                } else {
                    writer.write_all(b"^I").unwrap();
                    in_buf = &in_buf[p + 1..];
                    count += p + 1;
                }
            }
            None => {
                writer.write_all(in_buf).unwrap();
                return 0;
            }
        };
    }
}

fn write_nonprint_to_end<W: Write>(in_buf: &[u8], writer: &mut W, tab: &[u8]) -> usize {
    let mut count = 0;

    for byte in in_buf.iter().map(|c| *c) {
        if byte == b'\n' {
            break;
        }
        match byte {
            9 => writer.write_all(tab),
            0...8 | 10...31 => writer.write_all(&[b'^', byte + 64]),
            32...126 => writer.write_all(&[byte]),
            127 => writer.write_all(&[b'^', byte - 64]),
            128...159 => writer.write_all(&[b'M', b'-', b'^', byte - 64]),
            160...254 => writer.write_all(&[b'M', b'-', byte - 128]),
            _ => writer.write_all(&[b'M', b'-', b'^', 63]),
        }.unwrap();
        count += 1;
    }
    if count != in_buf.len() {
        count + 1
    } else {
        0
    }
}

mod tests {
    use std::io::{BufWriter, stdout};

    #[test]
    fn test_write_nonprint_to_end() {
        let mut writer = BufWriter::with_capacity(1024 * 64, stdout());
        let in_buf = [b'\n'];
        let tab: [u8; 0] = [];
        super::write_nonprint_to_end(&in_buf, &mut writer, &tab);
        assert_eq!(tab.len(), 0);
    }
}
<|endoftext|>