/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Data structure measurement. use libc::{c_void, size_t}; use std::collections::LinkedList; use std::mem::transmute; use std::sync::Arc; extern { // Get the size of a heap block. // // Ideally Rust would expose a function like this in std::rt::heap, which would avoid the // jemalloc dependence. // // The C prototype is `je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)`. On some // platforms `JEMALLOC_USABLE_SIZE_CONST` is `const` and on some it is empty. But in practice // this function doesn't modify the contents of the block that `ptr` points to, so we use // `*const c_void` here. fn je_malloc_usable_size(ptr: *const c_void) -> size_t; } // A wrapper for je_malloc_usable_size that handles `EMPTY` and returns `usize`. pub fn heap_size_of(ptr: *const c_void) -> usize { if ptr == ::std::rt::heap::EMPTY as *const c_void { 0 } else { unsafe { je_malloc_usable_size(ptr) as usize } } } // The simplest trait for measuring the size of heap data structures. More complex traits that // return multiple measurements -- e.g. measure text separately from images -- are also possible, // and should be used when appropriate. // // FIXME(njn): it would be nice to be able to derive this trait automatically, given that // implementations are mostly repetitive and mechanical. // pub trait SizeOf { /// Measure the size of any heap-allocated structures that hang off this value, but not the /// space taken up by the value itself (i.e. what size_of:: measures, more or less); that /// space is handled by the implementation of SizeOf for Box below. fn size_of_excluding_self(&self) -> usize; } // There are two possible ways to measure the size of `self` when it's on the heap: compute it // (with `::std::rt::heap::usable_size(::std::mem::size_of::(), 0)`) or measure it directly // using the heap allocator (with `heap_size_of`). We do the latter, for the following reasons. // // * The heap allocator is the true authority for the sizes of heap blocks; its measurement is // guaranteed to be correct. In comparison, size computations are error-prone. (For example, the // `rt::heap::usable_size` function used in some of Rust's non-default allocator implementations // underestimate the true usable size of heap blocks, which is safe in general but would cause // under-measurement here.) // // * If we measure something that isn't a heap block, we'll get a crash. This keeps us honest, // which is important because unsafe code is involved and this can be gotten wrong. // // However, in the best case, the two approaches should give the same results. // impl SizeOf for Box { fn size_of_excluding_self(&self) -> usize { // Measure size of `self`. heap_size_of(&**self as *const T as *const c_void) + (**self).size_of_excluding_self() } } impl SizeOf for String { fn size_of_excluding_self(&self) -> usize { heap_size_of(self.as_ptr() as *const c_void) } } impl SizeOf for Option { fn size_of_excluding_self(&self) -> usize { match *self { None => 0, Some(ref x) => x.size_of_excluding_self() } } } impl SizeOf for Arc { fn size_of_excluding_self(&self) -> usize { (**self).size_of_excluding_self() } } impl SizeOf for Vec { fn size_of_excluding_self(&self) -> usize { heap_size_of(self.as_ptr() as *const c_void) + self.iter().fold(0, |n, elem| n + elem.size_of_excluding_self()) } } // FIXME(njn): We can't implement SizeOf accurately for LinkedList because it requires access to the // private Node type. Eventually we'll want to add SizeOf (or equivalent) to Rust itself. In the // meantime, we use the dirty hack of transmuting LinkedList into an identical type (LinkedList2) and // measuring that. impl SizeOf for LinkedList { fn size_of_excluding_self(&self) -> usize { let list2: &LinkedList2 = unsafe { transmute(self) }; list2.size_of_excluding_self() } } struct LinkedList2 { _length: usize, list_head: Link, _list_tail: Rawlink>, } type Link = Option>>; struct Rawlink { _p: *mut T, } struct Node { next: Link, _prev: Rawlink>, value: T, } impl SizeOf for Node { // Unlike most size_of_excluding_self() functions, this one does *not* measure descendents. // Instead, LinkedList2::size_of_excluding_self() handles that, so that it can use iteration // instead of recursion, which avoids potentially blowing the stack. fn size_of_excluding_self(&self) -> usize { self.value.size_of_excluding_self() } } impl SizeOf for LinkedList2 { fn size_of_excluding_self(&self) -> usize { let mut size = 0; let mut curr: &Link = &self.list_head; while curr.is_some() { size += (*curr).size_of_excluding_self(); curr = &curr.as_ref().unwrap().next; } size } } // This is a basic sanity check. If the representation of LinkedList changes such that it becomes a // different size to LinkedList2, this will fail at compile-time. #[allow(dead_code)] unsafe fn linked_list2_check() { transmute::, LinkedList2>(panic!()); } // Currently, types that implement the Drop type are larger than those that don't. Because // LinkedList implements Drop, LinkedList2 must also so that linked_list2_check() doesn't fail. #[unsafe_destructor] impl Drop for LinkedList2 { fn drop(&mut self) {} }