forked from len0rd/rockbox
This commit was manufactured by cvs2svn to create tag 'v2_1'.
git-svn-id: svn://svn.rockbox.org/rockbox/tags/v2_1@4102 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
66d165819a
commit
2a1b031d62
34 changed files with 0 additions and 3981 deletions
|
|
@ -1,14 +0,0 @@
|
|||
# The "checkoutlist" file is used to support additional version controlled
|
||||
# administrative files in $CVSROOT/CVSROOT, such as template files.
|
||||
#
|
||||
# The first entry on a line is a filename which will be checked out from
|
||||
# the corresponding RCS file in the $CVSROOT/CVSROOT directory.
|
||||
# The remainder of the line is an error message to use if the file cannot
|
||||
# be checked out.
|
||||
#
|
||||
# File format:
|
||||
#
|
||||
# [<whitespace>]<filename><whitespace><error message><end-of-line>
|
||||
#
|
||||
# comment lines begin with '#'
|
||||
syncmail
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# The "commitinfo" file is used to control pre-commit checks.
|
||||
# The filter on the right is invoked with the repository and a list
|
||||
# of files to check. A non-zero exit of the filter program will
|
||||
# cause the commit to be aborted.
|
||||
#
|
||||
# The first entry on a line is a regular expression which is tested
|
||||
# against the directory that the change is being committed to, relative
|
||||
# to the $CVSROOT. For the first match that is found, then the remainder
|
||||
# of the line is the name of the filter to run.
|
||||
#
|
||||
# If the repository name does not match any of the regular expressions in this
|
||||
# file, the "DEFAULT" line is used, if it is specified.
|
||||
#
|
||||
# If the name "ALL" appears as a regular expression it is always used
|
||||
# in addition to the first matching regex or "DEFAULT".
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
# Set this to "no" if pserver shouldn't check system users/passwords
|
||||
#SystemAuth=no
|
||||
|
||||
# Set `PreservePermissions' to `yes' to save file status information
|
||||
# in the repository.
|
||||
#PreservePermissions=no
|
||||
|
||||
# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top
|
||||
# level of the new working directory when using the `cvs checkout'
|
||||
# command.
|
||||
#TopLevelAdmin=no
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# This file affects handling of files based on their names.
|
||||
#
|
||||
# The -t/-f options allow one to treat directories of files
|
||||
# as a single file, or to transform a file in other ways on
|
||||
# its way in and out of CVS.
|
||||
#
|
||||
# The -m option specifies whether CVS attempts to merge files.
|
||||
#
|
||||
# The -k option specifies keyword expansion (e.g. -kb for binary).
|
||||
#
|
||||
# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
|
||||
#
|
||||
# wildcard [option value][option value]...
|
||||
#
|
||||
# where option is one of
|
||||
# -f from cvs filter value: path to filter
|
||||
# -t to cvs filter value: path to filter
|
||||
# -m update methodology value: MERGE or COPY
|
||||
# -k expansion mode value: b, o, kkv, &c
|
||||
#
|
||||
# and value is a single-quote delimited value.
|
||||
# For example:
|
||||
#*.gif -k 'b'
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
# The "editinfo" file is used to allow verification of logging
|
||||
# information. It works best when a template (as specified in the
|
||||
# rcsinfo file) is provided for the logging procedure. Given a
|
||||
# template with locations for, a bug-id number, a list of people who
|
||||
# reviewed the code before it can be checked in, and an external
|
||||
# process to catalog the differences that were code reviewed, the
|
||||
# following test can be applied to the code:
|
||||
#
|
||||
# Making sure that the entered bug-id number is correct.
|
||||
# Validating that the code that was reviewed is indeed the code being
|
||||
# checked in (using the bug-id number or a seperate review
|
||||
# number to identify this particular code set.).
|
||||
#
|
||||
# If any of the above test failed, then the commit would be aborted.
|
||||
#
|
||||
# Actions such as mailing a copy of the report to each reviewer are
|
||||
# better handled by an entry in the loginfo file.
|
||||
#
|
||||
# One thing that should be noted is the the ALL keyword is not
|
||||
# supported. There can be only one entry that matches a given
|
||||
# repository.
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
# The "loginfo" file controls where "cvs commit" log information
|
||||
# is sent. The first entry on a line is a regular expression which must match
|
||||
# the directory that the change is being made to, relative to the
|
||||
# $CVSROOT. If a match is found, then the remainder of the line is a filter
|
||||
# program that should expect log information on its standard input.
|
||||
#
|
||||
# If the repository name does not match any of the regular expressions in this
|
||||
# file, the "DEFAULT" line is used, if it is specified.
|
||||
#
|
||||
# If the name ALL appears as a regular expression it is always used
|
||||
# in addition to the first matching regex or DEFAULT.
|
||||
#
|
||||
# You may specify a format string as part of the
|
||||
# filter. The string is composed of a `%' followed
|
||||
# by a single format character, or followed by a set of format
|
||||
# characters surrounded by `{' and `}' as separators. The format
|
||||
# characters are:
|
||||
#
|
||||
# s = file name
|
||||
# V = old version number (pre-checkin)
|
||||
# v = new version number (post-checkin)
|
||||
#
|
||||
# For example:
|
||||
#DEFAULT (echo ""; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog
|
||||
# or
|
||||
#DEFAULT (echo ""; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog
|
||||
CVSROOT $CVSROOT/CVSROOT/syncmail -C4 -u %{sVv} zagor@users.sourceforge.net
|
||||
^apps $CVSROOT/CVSROOT/syncmail -C4 -u %{sVv} rockbox-cvs@cool.haxx.se
|
||||
^flash $CVSROOT/CVSROOT/syncmail -C4 -u %{sVv} rockbox-cvs@cool.haxx.se
|
||||
^firmware $CVSROOT/CVSROOT/syncmail -C4 -u %{sVv} rockbox-cvs@cool.haxx.se
|
||||
^docs $CVSROOT/CVSROOT/syncmail -C4 -u %{sVv} rockbox-cvs@cool.haxx.se
|
||||
^uisimulator $CVSROOT/CVSROOT/syncmail -C4 -u %{sVv} rockbox-cvs@cool.haxx.se
|
||||
^tools $CVSROOT/CVSROOT/syncmail -C4 -u %{sVv} rockbox-cvs@cool.haxx.se
|
||||
^gdb $CVSROOT/CVSROOT/syncmail -C4 -u %{sVv} rockbox-cvs@cool.haxx.se
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# Three different line formats are valid:
|
||||
# key -a aliases...
|
||||
# key [options] directory
|
||||
# key [options] directory files...
|
||||
#
|
||||
# Where "options" are composed of:
|
||||
# -i prog Run "prog" on "cvs commit" from top-level of module.
|
||||
# -o prog Run "prog" on "cvs checkout" of module.
|
||||
# -e prog Run "prog" on "cvs export" of module.
|
||||
# -t prog Run "prog" on "cvs rtag" of module.
|
||||
# -u prog Run "prog" on "cvs update" of module.
|
||||
# -d dir Place module in directory "dir" instead of module name.
|
||||
# -l Top-level directory only -- do not recurse.
|
||||
#
|
||||
# NOTE: If you change any of the "Run" options above, you'll have to
|
||||
# release and re-checkout any working directories of these modules.
|
||||
#
|
||||
# And "directory" is a path to a directory relative to $CVSROOT.
|
||||
#
|
||||
# The "-a" option specifies an alias. An alias is interpreted as if
|
||||
# everything on the right of the "-a" had been typed on the command line.
|
||||
#
|
||||
# You can encode a module within a module by using the special '&'
|
||||
# character to interpose another module into the current module. This
|
||||
# can be useful for creating a module that consists of many directories
|
||||
# spread out over the entire source repository.
|
||||
|
||||
rockbox &apps &firmware &docs &tools
|
||||
rockbox-devel &apps &flash &firmware &docs &tools &uisimulator &gdb
|
||||
rockbox-all &apps &flash &firmware &docs &tools &uisimulator &gdb &www
|
||||
website &www &docs
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# The "notify" file controls where notifications from watches set by
|
||||
# "cvs watch add" or "cvs edit" are sent. The first entry on a line is
|
||||
# a regular expression which is tested against the directory that the
|
||||
# change is being made to, relative to the $CVSROOT. If it matches,
|
||||
# then the remainder of the line is a filter program that should contain
|
||||
# one occurrence of %s for the user to notify, and information on its
|
||||
# standard input.
|
||||
#
|
||||
# "ALL" or "DEFAULT" can be used in place of the regular expression.
|
||||
#
|
||||
# For example:
|
||||
#ALL mail %s -s "CVS notification"
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# The "rcsinfo" file is used to control templates with which the editor
|
||||
# is invoked on commit and import.
|
||||
#
|
||||
# The first entry on a line is a regular expression which is tested
|
||||
# against the directory that the change is being made to, relative to the
|
||||
# $CVSROOT. For the first match that is found, then the remainder of the
|
||||
# line is the name of the file that contains the template.
|
||||
#
|
||||
# If the repository name does not match any of the regular expressions in this
|
||||
# file, the "DEFAULT" line is used, if it is specified.
|
||||
#
|
||||
# If the name "ALL" appears as a regular expression it is always used
|
||||
# in addition to the first matching regex or "DEFAULT".
|
||||
210
CVSROOT/syncmail
210
CVSROOT/syncmail
|
|
@ -1,210 +0,0 @@
|
|||
#! /usr/bin/python
|
||||
# -*- Python -*-
|
||||
|
||||
"""Complicated notification for CVS checkins.
|
||||
|
||||
This script is used to provide email notifications of changes to the CVS
|
||||
repository. These email changes will include context diffs of the changes.
|
||||
Really big diffs will be trimmed.
|
||||
|
||||
This script is run from a CVS loginfo file (see $CVSROOT/CVSROOT/loginfo). To
|
||||
set this up, create a loginfo entry that looks something like this:
|
||||
|
||||
mymodule /path/to/this/script %%s some-email-addr@your.domain
|
||||
|
||||
In this example, whenever a checkin that matches `mymodule' is made, this
|
||||
script is invoked, which will generate the diff containing email, and send it
|
||||
to some-email-addr@your.domain.
|
||||
|
||||
Note: This module used to also do repository synchronizations via
|
||||
rsync-over-ssh, but since the repository has been moved to SourceForge,
|
||||
this is no longer necessary. The syncing functionality has been ripped
|
||||
out in the 3.0, which simplifies it considerably. Access the 2.x versions
|
||||
to refer to this functionality. Because of this, the script is misnamed.
|
||||
|
||||
It no longer makes sense to run this script from the command line. Doing so
|
||||
will only print out this usage information.
|
||||
|
||||
Usage:
|
||||
|
||||
%(PROGRAM)s [options] <%%S> email-addr [email-addr ...]
|
||||
|
||||
Where options is:
|
||||
|
||||
--cvsroot=<path>
|
||||
Use <path> as the environment variable CVSROOT. Otherwise this
|
||||
variable must exist in the environment.
|
||||
|
||||
--help
|
||||
-h
|
||||
Print this text.
|
||||
|
||||
--context=#
|
||||
-C #
|
||||
Include # lines of context around lines that differ (default: 2).
|
||||
|
||||
-c
|
||||
Produce a context diff (default).
|
||||
|
||||
-u
|
||||
Produce a unified diff (smaller, but harder to read).
|
||||
|
||||
<%%S>
|
||||
CVS %%s loginfo expansion. When invoked by CVS, this will be a single
|
||||
string containing the directory the checkin is being made in, relative
|
||||
to $CVSROOT, followed by the list of files that are changing. If the
|
||||
%%s in the loginfo file is %%{sVv}, context diffs for each of the
|
||||
modified files are included in any email messages that are generated.
|
||||
|
||||
email-addrs
|
||||
At least one email address.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import string
|
||||
import time
|
||||
import getopt
|
||||
|
||||
# Notification command
|
||||
MAILCMD = '/bin/mail -s "cvs: %(SUBJECT)s" %(PEOPLE)s 2>&1 > /dev/null'
|
||||
|
||||
# Diff trimming stuff
|
||||
DIFF_HEAD_LINES = 20
|
||||
DIFF_TAIL_LINES = 20
|
||||
DIFF_TRUNCATE_IF_LARGER = 1000
|
||||
|
||||
PROGRAM = sys.argv[0]
|
||||
|
||||
|
||||
|
||||
def usage(code, msg=''):
|
||||
print __doc__ % globals()
|
||||
if msg:
|
||||
print msg
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
|
||||
def calculate_diff(filespec, contextlines):
|
||||
try:
|
||||
file, oldrev, newrev = string.split(filespec, ',')
|
||||
except ValueError:
|
||||
# No diff to report
|
||||
return '***** Bogus filespec: %s' % filespec
|
||||
if oldrev == 'NONE':
|
||||
try:
|
||||
if os.path.exists(file):
|
||||
fp = open(file)
|
||||
else:
|
||||
update_cmd = 'cvs -fn update -r %s -p %s' % (newrev, file)
|
||||
fp = os.popen(update_cmd)
|
||||
lines = fp.readlines()
|
||||
fp.close()
|
||||
lines.insert(0, '--- NEW FILE: %s ---\n' % file)
|
||||
except IOError, e:
|
||||
lines = ['***** Error reading new file: ',
|
||||
str(e), '\n***** file: ', file, ' cwd: ', os.getcwd()]
|
||||
elif newrev == 'NONE':
|
||||
lines = ['--- %s DELETED ---\n' % file]
|
||||
else:
|
||||
# This /has/ to happen in the background, otherwise we'll run into CVS
|
||||
# lock contention. What a crock.
|
||||
if contextlines > 0:
|
||||
difftype = "-C " + str(contextlines)
|
||||
else:
|
||||
difftype = "-uN"
|
||||
diffcmd = '/usr/bin/cvs -f diff -kk %s -b -r %s -r %s %s' % (
|
||||
difftype, oldrev, newrev, file)
|
||||
fp = os.popen(diffcmd)
|
||||
lines = fp.readlines()
|
||||
sts = fp.close()
|
||||
# ignore the error code, it always seems to be 1 :(
|
||||
## if sts:
|
||||
## return 'Error code %d occurred during diff\n' % (sts >> 8)
|
||||
if len(lines) > DIFF_TRUNCATE_IF_LARGER:
|
||||
removedlines = len(lines) - DIFF_HEAD_LINES - DIFF_TAIL_LINES
|
||||
del lines[DIFF_HEAD_LINES:-DIFF_TAIL_LINES]
|
||||
lines.insert(DIFF_HEAD_LINES,
|
||||
'[...%d lines suppressed...]\n' % removedlines)
|
||||
return string.join(lines, '')
|
||||
|
||||
|
||||
|
||||
def blast_mail(mailcmd, filestodiff, contextlines):
|
||||
# cannot wait for child process or that will cause parent to retain cvs
|
||||
# lock for too long. Urg!
|
||||
if not os.fork():
|
||||
# in the child
|
||||
# give up the lock you cvs thang!
|
||||
time.sleep(2)
|
||||
fp = os.popen(mailcmd, 'w')
|
||||
fp.write(sys.stdin.read())
|
||||
fp.write('\n')
|
||||
# append the diffs if available
|
||||
for file in filestodiff:
|
||||
fp.write(calculate_diff(file, contextlines))
|
||||
fp.write('\n')
|
||||
fp.close()
|
||||
# doesn't matter what code we return, it isn't waited on
|
||||
os._exit(0)
|
||||
|
||||
|
||||
|
||||
# scan args for options
|
||||
def main():
|
||||
contextlines = 2
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'hC:cu',
|
||||
['context=', 'cvsroot=', 'help'])
|
||||
except getopt.error, msg:
|
||||
usage(1, msg)
|
||||
|
||||
# parse the options
|
||||
for opt, arg in opts:
|
||||
if opt in ('-h', '--help'):
|
||||
usage(0)
|
||||
elif opt == '--cvsroot':
|
||||
os.environ['CVSROOT'] = arg
|
||||
elif opt in ('-C', '--context'):
|
||||
contextlines = int(arg)
|
||||
elif opt == '-c':
|
||||
if contextlines <= 0:
|
||||
contextlines = 2
|
||||
elif opt == '-u':
|
||||
contextlines = 0
|
||||
|
||||
# What follows is the specification containing the files that were
|
||||
# modified. The argument actually must be split, with the first component
|
||||
# containing the directory the checkin is being made in, relative to
|
||||
# $CVSROOT, followed by the list of files that are changing.
|
||||
if not args:
|
||||
usage(1, 'No CVS module specified')
|
||||
SUBJECT = args[0]
|
||||
specs = string.split(args[0])
|
||||
del args[0]
|
||||
|
||||
# The remaining args should be the email addresses
|
||||
if not args:
|
||||
usage(1, 'No recipients specified')
|
||||
|
||||
# Now do the mail command
|
||||
PEOPLE = string.join(args)
|
||||
mailcmd = MAILCMD % vars()
|
||||
|
||||
print 'Mailing %s...' % PEOPLE
|
||||
if specs == ['-', 'Imported', 'sources']:
|
||||
return
|
||||
if specs[-3:] == ['-', 'New', 'directory']:
|
||||
del specs[-3:]
|
||||
print 'Generating notification message...'
|
||||
blast_mail(mailcmd, specs[1:], contextlines)
|
||||
print 'Generating notification message... done.'
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
sys.exit(0)
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# The "taginfo" file is used to control pre-tag checks.
|
||||
# The filter on the right is invoked with the following arguments:
|
||||
#
|
||||
# $1 -- tagname
|
||||
# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d
|
||||
# $3 -- repository
|
||||
# $4-> file revision [file revision ...]
|
||||
#
|
||||
# A non-zero exit of the filter program will cause the tag to be aborted.
|
||||
#
|
||||
# The first entry on a line is a regular expression which is tested
|
||||
# against the directory that the change is being committed to, relative
|
||||
# to the $CVSROOT. For the first match that is found, then the remainder
|
||||
# of the line is the name of the filter to run.
|
||||
#
|
||||
# If the repository name does not match any of the regular expressions in this
|
||||
# file, the "DEFAULT" line is used, if it is specified.
|
||||
#
|
||||
# If the name "ALL" appears as a regular expression it is always used
|
||||
# in addition to the first matching regex or "DEFAULT".
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
# The "verifymsg" file is used to allow verification of logging
|
||||
# information. It works best when a template (as specified in the
|
||||
# rcsinfo file) is provided for the logging procedure. Given a
|
||||
# template with locations for, a bug-id number, a list of people who
|
||||
# reviewed the code before it can be checked in, and an external
|
||||
# process to catalog the differences that were code reviewed, the
|
||||
# following test can be applied to the code:
|
||||
#
|
||||
# Making sure that the entered bug-id number is correct.
|
||||
# Validating that the code that was reviewed is indeed the code being
|
||||
# checked in (using the bug-id number or a seperate review
|
||||
# number to identify this particular code set.).
|
||||
#
|
||||
# If any of the above test failed, then the commit would be aborted.
|
||||
#
|
||||
# Actions such as mailing a copy of the report to each reviewer are
|
||||
# better handled by an entry in the loginfo file.
|
||||
#
|
||||
# One thing that should be noted is the the ALL keyword is not
|
||||
# supported. There can be only one entry that matches a given
|
||||
# repository.
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
# __________ __ ___.
|
||||
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
# \/ \/ \/ \/ \/
|
||||
# $Id$
|
||||
#
|
||||
# Copyright (C) 2002 by Daniel Stenberg
|
||||
#
|
||||
# All files in this archive are subject to the GNU General Public License.
|
||||
# See the file COPYING in the source tree root for full license agreement.
|
||||
#
|
||||
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
# KIND, either express or implied.
|
||||
#
|
||||
|
||||
TARGET = libdmalloc.a
|
||||
|
||||
LIBOBJS = dmalloc.o bmalloc.o bysize.o
|
||||
|
||||
# define this to talk a lot in runtime
|
||||
# -DDEBUG_VERBOSE
|
||||
CFLAGS = -g -W -Wall -DDEBUG_MALLOC
|
||||
CC = gcc
|
||||
AR = ar
|
||||
|
||||
LDFLAGS = -L. -ldmalloc
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
clean:
|
||||
rm -f core *~ $(TARGET) $(LIBOBJS)
|
||||
|
||||
$(TARGET): $(LIBOBJS)
|
||||
$(AR) ruv $(TARGET) $(LIBOBJS)
|
||||
|
||||
bmalloc.o: bmalloc.c bysize.h
|
||||
bysize.o: bysize.c
|
||||
dmalloc.o: dmalloc.c
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
Package: dbestfit - a dynamic best-fit memory allocator
|
||||
Date: 1996 - 2002
|
||||
Version: 3.3
|
||||
Author: Daniel Stenberg <daniel@haxx.se>
|
||||
License: MIT originally, files in the Rockbox project are GPL licensed.
|
||||
|
||||
I wrote the dmalloc part for small allocation sizes to improve the behavior
|
||||
of the built-in (first-fit) allocator found in pSOS, during late 1996 and
|
||||
spring 1997.
|
||||
|
||||
I wrote the bmalloc part (best-fit with optional splay-tree sorting) just for
|
||||
the fun of it and to see how good malloc() implementation I could make. The
|
||||
quality of my implementation is still left to be judged in real-world tests.
|
||||
|
||||
TODO:
|
||||
* Remove the final not-so-very-nice loop in dmalloc.c that checks for a block
|
||||
with free fragments (when the list gets longer too much time might be spent
|
||||
in that loop).
|
||||
|
||||
* Make a separate application that samples the memory usage of a program
|
||||
and is capable of replaying it (in order to test properly).
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
====================================
|
||||
Memory Allocation Algorithm Theories
|
||||
====================================
|
||||
|
||||
GOAL
|
||||
It is intended to be a 100% working memory allocation system. It should be
|
||||
capable of replacing an ordinary Operating System's own routines. It should
|
||||
work good in a multitasking, shared memory, non-virtual memory environment
|
||||
without clogging the memory. Primary aimed for small machines, CPUs and
|
||||
memory amounts.
|
||||
|
||||
I use a best-fit algorithm with a slight overhead in order to increase speed
|
||||
a lot. It should remain scalable and work good with very large amount of
|
||||
memory and free/used memory blocks too.
|
||||
|
||||
TERMINOLOGY
|
||||
|
||||
FRAGMENT - small identically sized parts of a larger BLOCK, they are _not_
|
||||
allocated when traversed in lists etc
|
||||
BLOCK - large memory area, if used for FRAGMENTS, they are linked in a
|
||||
lists. One list for each FRAGMENT size supported.
|
||||
TOP - head struct that holds information about and points to a chain
|
||||
of BLOCKS for a particular FRAGMENT size.
|
||||
CHUNK - a contiguous area of free memory
|
||||
|
||||
MEMORY SYSTEM
|
||||
|
||||
We split the system in two parts. One part allocates small memory amounts
|
||||
and one part allocates large memory amounts, but all allocations are done
|
||||
"through" the small-part-system. There is an option to use only the small
|
||||
system (and thus use the OS for large blocks) or the complete package.
|
||||
|
||||
##############################################################################
|
||||
SMALL SIZE ALLOCATIONS
|
||||
##############################################################################
|
||||
|
||||
Keywords for this system is 'Deferred Coalescing' and 'quick lists'.
|
||||
|
||||
ALLOC
|
||||
|
||||
* Small allocations are "aligned" upwards to a set of preset sizes. In the
|
||||
current implementation I use 20, 28, 52, 116, 312, 580, 1016, 2032 bytes.
|
||||
Memory allocations of these sizes are referred to as FRAGMENTS.
|
||||
(The reason for these specific sizes is the requirement that they must be
|
||||
32-bit aligned and fit as good as possible within 4064 bytes.)
|
||||
|
||||
* Allocations larger than 2032 will get a BLOCK for that allocation only.
|
||||
|
||||
* Each of these sizes has it's own TOP. When a FRAGMENT is requested, a
|
||||
larger BLOCK will be allocated and divided into many FRAGMENTS (all of the
|
||||
same size). TOP points to a list with BLOCKS that contains FRAGMENTS of
|
||||
the same size. Each BLOCK has a 'number of free FRAGMENTS' counter and so
|
||||
has each TOP (for the entire chain).
|
||||
|
||||
* A BLOCK is around 4064 bytes plus the size of the information header. This
|
||||
size is adjusted to make the allocation of the big block not require more
|
||||
than 4096 bytes. (This might not be so easy to be sure of, if you don't
|
||||
know how the big-block system works, but the BMALLOC system uses an
|
||||
extra header of 12 bytes and the header for the FRAGMENT BLOCK is 20 bytes
|
||||
in a general 32-bit environment.)
|
||||
|
||||
* In case the allocation of a BLOCK fails when a FRAGMENT is required, the
|
||||
next size of FRAGMENTS will be checked for a free FRAGMENT. First when the
|
||||
larger size lists have been tested without success it will fail for real.
|
||||
|
||||
FREE
|
||||
|
||||
* When FRAGMENTS are freed so that a BLOCK becomes non-used, it is returned
|
||||
to the system.
|
||||
|
||||
* FREEing a fragment adds the buffer in a LIFO-order. That means that the
|
||||
next request for a fragment from the same list, the last freed buffer will
|
||||
be returned first.
|
||||
|
||||
REALLOC
|
||||
|
||||
* REALLOCATION of a FRAGMENT does first check if the new size would fit
|
||||
within the same FRAGMENT and if it would use the same FRAGMENT size. If it
|
||||
does and would, the same pointer is returned.
|
||||
|
||||
OVERHEAD
|
||||
|
||||
Yes, there is an overhead on small allocations (internal fragmentation).
|
||||
Yet, I do believe that small allocations more often than larger ones are
|
||||
used dynamically. I believe that a large overhead is not a big problem if it
|
||||
remains only for a while. The big gain is with the extreme speed we can GET
|
||||
and RETURN small allocations. This has yet to be proven. I am open to other
|
||||
systems of dealing with the small ones, but I don`t believe in using the
|
||||
same system for all sizes of allocations.
|
||||
|
||||
IMPROVEMENT
|
||||
|
||||
An addition to the above described algorithm is the `save-empty-BLOCKS-a-
|
||||
while-afterwards`. It will be used when the last used FRAGMENT within a
|
||||
BLOCK is freed. The BLOCK will then not get returned to the system until "a
|
||||
few more" FRAGMENTS have been freed in case the last [few] freed FRAGMENTS
|
||||
are allocated yet again (and thus prevent the huge overhead of making
|
||||
FRAGMENTS in a BLOCK). The "only" drawback of such a SEBAWA concept is
|
||||
that it would mean an even bigger overhead...
|
||||
|
||||
HEADERS (in allocated data)
|
||||
|
||||
FRAGMENTS - 32-bit pointer to its parent BLOCK (lowest bit must be 0)
|
||||
BLOCK - 32-bit size (lowest bit must be 1 to separate this from
|
||||
FRAGMENTS)
|
||||
|
||||
##############################################################################
|
||||
LARGER ALLOCATIONS
|
||||
##############################################################################
|
||||
|
||||
If the requested size is larger than the largest FRAGMENT size supported,
|
||||
the allocation will be made for this memory area alone, or if a BLOCK is
|
||||
allocated to fit lots of FRAGMENTS a large block is also desired.
|
||||
|
||||
* We add memory to the "system" with the add_pool() function call. It
|
||||
specifies the start and size of the new block of memory that will be
|
||||
used in this memory allocation system. Several add_pool() calls are
|
||||
supported and they may or may not add contiguous memory.
|
||||
|
||||
* Make all blocks get allocated aligned to BLOCKSIZE (sometimes referred to
|
||||
as 'grain size'), 64 bytes in my implementation. Reports tell us there is
|
||||
no real gain in increasing the size of the align.
|
||||
|
||||
* We link *all* pieces of memory (AREAS), free or not free. We keep the list
|
||||
in address order and thus when a FREE() occurs we know instantly if there
|
||||
are FREE CHUNKS wall-to-wall. No list "travels" needed. Requires some
|
||||
extra space in every allocated BLOCK. Still needs to put the new CHUNK in
|
||||
the right place in size-sorted list/tree. All memory areas, allocated or
|
||||
not, contain the following header:
|
||||
- size of this memory area (31 bits)
|
||||
- FREE status (1 bit)
|
||||
- pointer to the next AREA closest in memory (32 bits)
|
||||
- pointer to the prev AREA closest in memory (32 bits)
|
||||
(Totally 12 bytes)
|
||||
|
||||
* Sort all FREE CHUNKS in size-order. We use a SPLAY TREE algorithm for
|
||||
maximum speed. Data/structs used for the size-sorting functions are kept
|
||||
in an abstraction layer away from this since it is really not changing
|
||||
anything (except executing speed).
|
||||
|
||||
ALLOC (RSIZE - requested size, aligned properly)
|
||||
|
||||
* Fetch a CHUNK that RSIZE fits within. If the found CHUNK is larger than
|
||||
RSIZE, split it and return the RSIZE to the caller. Link the new CHUNK
|
||||
into the list/tree.
|
||||
|
||||
FREE (AREA - piece of memory that is returned to the system)
|
||||
|
||||
* Since the allocated BLOCK has kept its link-pointers, we can without
|
||||
checking any list instantly see if there are any FREE CHUNKS that are
|
||||
wall-to-wall with the AREA (both sides). If the AREA *is* wall-to-wall
|
||||
with one or two CHUNKS that or they are unlinked from the lists, enlarged
|
||||
and re-linked into the lists.
|
||||
|
||||
REALLOC
|
||||
|
||||
* There IS NO realloc() of large blocks, they are performed in the previous
|
||||
layer (dmalloc).
|
||||
|
||||
|
||||
##############################################################################
|
||||
FURTHER READING
|
||||
##############################################################################
|
||||
|
||||
* "Dynamic Storage Allocation: A Survey and Critical Review" (Paul R. Wilson,
|
||||
Mark S. Johnstone, Michael Neely, David Boles)
|
||||
ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps
|
||||
|
||||
* "A Memory Allocator" (Doug Lea)
|
||||
http://g.oswego.edu/dl/html/malloc.html
|
||||
|
|
@ -1,386 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by Daniel Stenberg
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Big (best-fit) Memory Allocation
|
||||
*
|
||||
* Author: Daniel Stenberg <daniel@haxx.se>
|
||||
*
|
||||
* Read THOUGHTS for theories and details on implementation.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "bysize.h"
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
/* #define DEBUG_MALLOC */
|
||||
|
||||
#define BMEM_ALIGN 64 /* resolution */
|
||||
|
||||
#define BMEMERR_TOOSMALL -1
|
||||
|
||||
/* this struct will be stored in all CHUNKS and AREAS */
|
||||
struct BlockInfo {
|
||||
struct BlockInfo *lower; /* previous block in memory (lower address) */
|
||||
struct BlockInfo *higher; /* next block in memory (higher address) */
|
||||
unsigned long info; /* 31 bits size: 1 bit free boolean */
|
||||
#define INFO_FREE 1
|
||||
#define INFO_SIZE (~ INFO_FREE) /* inverted FREE bit pattern */
|
||||
|
||||
/* FREE+SIZE Could be written to use ordinary bitfields if using a smart
|
||||
(like gcc) compiler in a manner like:
|
||||
int size:31;
|
||||
int free:1;
|
||||
|
||||
The 'higher' pointer COULD be removed completely if the size is used as
|
||||
an index to the higher one. This would then REQUIRE the entire memory
|
||||
pool to be contiguous and it needs a 'terminating' "node" or an extra
|
||||
flag that informs about the end of the list.
|
||||
*/
|
||||
};
|
||||
|
||||
/* the BLOCK list should be sorted in a lower to higher address order */
|
||||
struct BlockInfo *blockHead=NULL; /* nothing from the start */
|
||||
|
||||
void bmalloc_status(void);
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* remove_block()
|
||||
*
|
||||
* Remove the block from the address-sorted list.
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
static
|
||||
void remove_block(struct BlockInfo *block)
|
||||
{
|
||||
if(block->lower)
|
||||
block->lower->higher = block->higher;
|
||||
else
|
||||
blockHead = block->higher;
|
||||
if(block->higher)
|
||||
block->higher->lower = block->lower;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* add_blocktolists()
|
||||
*
|
||||
* Adds the specified block at the specified place in the address-sorted
|
||||
* list and at the appropriate place in the size-sorted.
|
||||
*
|
||||
***************************************************************************/
|
||||
static
|
||||
void add_blocktolists(struct BlockInfo *block,
|
||||
struct BlockInfo *newblock,
|
||||
size_t newsize)
|
||||
{
|
||||
struct BlockInfo *temp; /* temporary storage variable */
|
||||
if(block) {
|
||||
/* `block' is now a lower address than 'newblock' */
|
||||
|
||||
/*
|
||||
* Check if the new CHUNK is wall-to-wall with the lower addressed
|
||||
* one (if *that* is free)
|
||||
*/
|
||||
if(block->info&INFO_FREE) {
|
||||
if((char *)block + (block->info&INFO_SIZE) == (char *)newblock) {
|
||||
/* yes sir, this is our lower address neighbour, enlarge that one
|
||||
pick it out from the list and recursively add that chunk and
|
||||
then we escape */
|
||||
|
||||
/* remove from size-sorted list: */
|
||||
bmalloc_remove_chunksize((char*)block+sizeof(struct BlockInfo));
|
||||
|
||||
block->info += newsize; /* newsize is an even number and thus the FREE
|
||||
bit is untouched */
|
||||
|
||||
remove_block(block); /* unlink the block address-wise */
|
||||
|
||||
/* recursively check our lower friend(s) */
|
||||
add_blocktolists(block->lower, block, block->info&INFO_SIZE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
temp = block->higher;
|
||||
|
||||
block->higher = newblock;
|
||||
newblock->lower = block;
|
||||
newblock->higher = temp;
|
||||
}
|
||||
else {
|
||||
/* this block should preceed the heading one */
|
||||
temp = blockHead;
|
||||
|
||||
/* check if this is our higher addressed neighbour */
|
||||
if((char *)newblock + newsize == (char *)temp) {
|
||||
|
||||
/* yes, we are wall-to-wall with the higher CHUNK */
|
||||
if(temp->info&INFO_FREE) {
|
||||
/* and the neighbour is even free, remove that one and enlarge
|
||||
ourselves, call add_blocktolists() recursively and then escape */
|
||||
|
||||
remove_block(temp); /* unlink 'temp' from list */
|
||||
|
||||
/* remove from size-sorted list: */
|
||||
bmalloc_remove_chunksize((char*)temp+sizeof(struct BlockInfo) );
|
||||
|
||||
/* add the upper block's size on ourselves */
|
||||
newsize += temp->info&INFO_SIZE;
|
||||
|
||||
/* add the new, bigger block */
|
||||
add_blocktolists(block, newblock, newsize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
blockHead = newblock;
|
||||
newblock->higher = temp;
|
||||
newblock->lower = NULL; /* there is no lower one */
|
||||
}
|
||||
|
||||
newblock->info = newsize | INFO_FREE; /* we do assume size isn't using the
|
||||
FREE bit */
|
||||
bmalloc_insert_bysize((char *)newblock+sizeof(struct BlockInfo), newsize);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* findblockbyaddr()
|
||||
*
|
||||
* Find the block that is just before the input block in memory. Returns NULL
|
||||
* if none is.
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
static
|
||||
struct BlockInfo *findblockbyaddr(struct BlockInfo *block)
|
||||
{
|
||||
struct BlockInfo *test = blockHead;
|
||||
struct BlockInfo *lower = NULL;
|
||||
|
||||
while(test && (test < block)) {
|
||||
lower = test;
|
||||
test = test->higher;
|
||||
}
|
||||
return lower;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* bmalloc_add_pool()
|
||||
*
|
||||
* This function should be the absolutely first function to call. It sets up
|
||||
* the memory bounds of the [first] CHUNK(s). It is possible to call this
|
||||
* function several times to add more CHUNKs to the pool of free memory. This
|
||||
* allows the bmalloc system to deal with non-contigous memory areas.
|
||||
*
|
||||
* Returns non-zero if an error occured. The memory was not added then.
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
int bmalloc_add_pool(void *start,
|
||||
size_t size)
|
||||
{
|
||||
struct BlockInfo *newblock = (struct BlockInfo *)start;
|
||||
struct BlockInfo *block;
|
||||
|
||||
if(size < BMEM_ALIGN)
|
||||
return BMEMERR_TOOSMALL;
|
||||
|
||||
block = findblockbyaddr( newblock );
|
||||
/* `block' is now a lower address than 'newblock' or NULL */
|
||||
|
||||
if(size&1)
|
||||
size--; /* only add even sizes */
|
||||
|
||||
add_blocktolists(block, newblock, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
static void bmalloc_failed(size_t size)
|
||||
{
|
||||
printf("*** " __FILE__ " Couldn't allocate %d bytes\n", size);
|
||||
bmalloc_status();
|
||||
}
|
||||
#else
|
||||
#define bmalloc_failed(x)
|
||||
#endif
|
||||
|
||||
void bmalloc_status(void)
|
||||
{
|
||||
#ifdef DEBUG_MALLOC
|
||||
struct BlockInfo *block = blockHead;
|
||||
long mem_free = 0;
|
||||
long mem_used = 0;
|
||||
|
||||
printf("List of BLOCKS (in address order):\n");
|
||||
while(block) {
|
||||
printf(" START %p END %p SIZE %ld FLAG %s\n",
|
||||
block,
|
||||
(char *)block+(block->info&INFO_SIZE),
|
||||
block->info&INFO_SIZE,
|
||||
(block->info&INFO_FREE)?"free":"used");
|
||||
if(block->info&INFO_FREE)
|
||||
mem_free += block->info&INFO_SIZE;
|
||||
else
|
||||
mem_used += block->info&INFO_SIZE;
|
||||
block = block->higher;
|
||||
}
|
||||
printf(" Used mem: %ld , free mem: %ld (total %ld)\n",
|
||||
mem_used, mem_free, mem_used + mem_free);
|
||||
bmalloc_print_sizes();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void *bmalloc(size_t size)
|
||||
{
|
||||
void *mem;
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
{
|
||||
static int count=0;
|
||||
int realsize = size + sizeof(struct BlockInfo);
|
||||
if(realsize%4096)
|
||||
realsize = ((size / BMEM_ALIGN)+1) * BMEM_ALIGN;
|
||||
printf("%d bmalloc(%d) [%d]\n", count++, size, realsize);
|
||||
}
|
||||
#endif
|
||||
|
||||
size += sizeof(struct BlockInfo); /* add memory for our header */
|
||||
|
||||
if(size&(BMEM_ALIGN-1)) /* a lot faster than %BMEM_ALIGN but this MUST be
|
||||
changed if the BLOCKSIZE is not 2^X ! */
|
||||
size = ((size / BMEM_ALIGN)+1) * BMEM_ALIGN; /* align like this */
|
||||
|
||||
/* get a CHUNK from the list with this size */
|
||||
mem = bmalloc_obtainbysize ( size );
|
||||
if(mem) {
|
||||
/* the memory block we have got is the "best-fit" and it is already
|
||||
un-linked from the free list */
|
||||
|
||||
/* now do the math to get the proper block pointer */
|
||||
struct BlockInfo *block= (struct BlockInfo *)
|
||||
((char *)mem - sizeof(struct BlockInfo));
|
||||
|
||||
block->info &= ~INFO_FREE;
|
||||
/* not free anymore */
|
||||
|
||||
if( size != (block->info&INFO_SIZE)) {
|
||||
/* split this chunk into two pieces and return the one that fits us */
|
||||
size_t othersize = (block->info&INFO_SIZE) - size;
|
||||
|
||||
if(othersize > BMEM_ALIGN) {
|
||||
/* prevent losing small pieces of memory due to weird alignments
|
||||
of the memory pool */
|
||||
|
||||
block->info = size; /* set new size (leave FREE bit cleared) */
|
||||
|
||||
/* Add the new chunk to the lists: */
|
||||
add_blocktolists(block->lower,
|
||||
(struct BlockInfo *)((char *)block + size),
|
||||
othersize );
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the memory our parent may use: */
|
||||
return (char *)block+sizeof(struct BlockInfo);
|
||||
}
|
||||
else {
|
||||
bmalloc_failed(size);
|
||||
return NULL; /* can't find any memory, fail hard */
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
bmalloc_status();
|
||||
#endif
|
||||
return mem;
|
||||
}
|
||||
|
||||
void bfree(void *ptr)
|
||||
{
|
||||
struct BlockInfo *block = (struct BlockInfo *)
|
||||
((char *)ptr - sizeof(struct BlockInfo));
|
||||
size_t size;
|
||||
|
||||
/* setup our initial higher and lower pointers */
|
||||
struct BlockInfo *lower = block->lower;
|
||||
struct BlockInfo *higher = block->higher;
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
static int freecount=0;
|
||||
printf("%d bfree(%p)\n", freecount++, ptr);
|
||||
#endif
|
||||
/* bind together lower addressed FREE CHUNKS */
|
||||
if(lower && (lower->info&INFO_FREE) &&
|
||||
((char *)lower + (lower->info&INFO_SIZE) == (char *)block)) {
|
||||
size = block->info&INFO_SIZE; /* original size */
|
||||
|
||||
/* remove from size-link: */
|
||||
bmalloc_remove_chunksize((char *)lower+sizeof(struct BlockInfo));
|
||||
|
||||
remove_block(block); /* unlink from address list */
|
||||
block = lower; /* new base area pointer */
|
||||
block->info += size; /* append the new size (the FREE bit
|
||||
will remain untouched) */
|
||||
|
||||
lower = lower->lower; /* new lower pointer */
|
||||
}
|
||||
/* bind together higher addressed FREE CHUNKS */
|
||||
if(higher && (higher->info&INFO_FREE) &&
|
||||
((char *)block + (block->info&INFO_SIZE) == (char *)higher)) {
|
||||
/* append higher size, the FREE bit won't be affected */
|
||||
block->info += (higher->info&INFO_SIZE);
|
||||
|
||||
/* unlink from size list: */
|
||||
bmalloc_remove_chunksize(higher+sizeof(struct BlockInfo));
|
||||
remove_block(higher); /* unlink from address list */
|
||||
higher = higher->higher; /* the new higher link */
|
||||
block->higher = higher; /* new higher link */
|
||||
}
|
||||
block->info &= ~INFO_FREE; /* consider this FREE! */
|
||||
|
||||
block->lower = lower;
|
||||
block->higher = higher;
|
||||
|
||||
bmalloc_insert_bysize((char *)block+sizeof(struct BlockInfo),
|
||||
block->info&INFO_SIZE);
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
bmalloc_status();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by Daniel Stenberg
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef _BMALLOC_H_
|
||||
#define _BMALLOC_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int bmalloc_add_pool(void *start, size_t size);
|
||||
void bmalloc_status(void);
|
||||
|
||||
void *bmalloc(size_t size);
|
||||
void bfree(void *ptr);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,451 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by Daniel Stenberg
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Size-sorted list/tree functions.
|
||||
*
|
||||
* Author: Daniel Stenberg
|
||||
* Date: March 7, 1997
|
||||
* Version: 2.0
|
||||
* Email: daniel@haxx.se
|
||||
*
|
||||
* v2.0
|
||||
* - Added SPLAY TREE functionality.
|
||||
*
|
||||
* Adds and removes CHUNKS from a list or tree.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SPLAY /* we use the splay version as that is much faster when the
|
||||
amount of blocks grow */
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef SPLAY /* these routines are for the non-splay version */
|
||||
|
||||
struct ChunkInfo {
|
||||
struct ChunkInfo *larger;
|
||||
struct ChunkInfo *smaller;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/* the CHUNK list anchor */
|
||||
struct ChunkInfo *chunkHead=NULL;
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
findchunkbysize()
|
||||
|
||||
Find the chunk that is smaller than the input size. Returns
|
||||
NULL if none is.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
static struct ChunkInfo *findchunkbysize(size_t size)
|
||||
{
|
||||
struct ChunkInfo *test = chunkHead;
|
||||
struct ChunkInfo *smaller = NULL;
|
||||
while(test && (test->size < size)) {
|
||||
smaller = test;
|
||||
test = test->larger;
|
||||
}
|
||||
return smaller;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
remove_chunksize()
|
||||
|
||||
Remove the chunk from the size-sorted list.
|
||||
***********************************************************************/
|
||||
|
||||
void bmalloc_remove_chunksize(void *data)
|
||||
{
|
||||
struct ChunkInfo *chunk = (struct ChunkInfo *)data;
|
||||
if(chunk->smaller)
|
||||
chunk->smaller->larger = chunk->larger;
|
||||
else {
|
||||
/* if this has no smaller, this is the head */
|
||||
chunkHead = chunk->larger; /* new head */
|
||||
}
|
||||
if(chunk->larger)
|
||||
chunk->larger->smaller = chunk->smaller;
|
||||
}
|
||||
|
||||
void bmalloc_insert_bysize(char *data, size_t size)
|
||||
{
|
||||
struct ChunkInfo *newchunk = (struct ChunkInfo *)data;
|
||||
struct ChunkInfo *chunk = findchunkbysize ( size );
|
||||
|
||||
newchunk->size = size;
|
||||
|
||||
if(chunk) {
|
||||
/* 'chunk' is smaller than size, append the new chunk ahead of this */
|
||||
newchunk->smaller = chunk;
|
||||
newchunk->larger = chunk->larger;
|
||||
if(chunk->larger)
|
||||
chunk->larger->smaller = newchunk;
|
||||
chunk->larger = newchunk;
|
||||
}
|
||||
else {
|
||||
/* smallest CHUNK around, append first in the list */
|
||||
newchunk->larger = chunkHead;
|
||||
newchunk->smaller = NULL;
|
||||
|
||||
if(chunkHead)
|
||||
chunkHead->smaller = newchunk;
|
||||
chunkHead = newchunk;
|
||||
}
|
||||
}
|
||||
|
||||
char *bmalloc_obtainbysize( size_t size)
|
||||
{
|
||||
struct ChunkInfo *chunk = findchunkbysize( size );
|
||||
|
||||
if(!chunk) {
|
||||
if(size <= (chunkHead->size))
|
||||
/* there is no smaller CHUNK, use the first one (if we fit within that)
|
||||
*/
|
||||
chunk = chunkHead;
|
||||
}
|
||||
else
|
||||
/* we're on the last CHUNK that is smaller than requested, step onto
|
||||
the bigger one */
|
||||
chunk = chunk->larger;
|
||||
|
||||
if(chunk) {
|
||||
bmalloc_remove_chunksize( chunk ); /* unlink size-wise */
|
||||
return (char *)chunk;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bmalloc_print_sizes(void)
|
||||
{
|
||||
struct ChunkInfo *chunk = chunkHead;
|
||||
printf("List of CHUNKS (in size order):\n");
|
||||
#if 1
|
||||
while(chunk) {
|
||||
printf(" START %p END %p SIZE %d\n",
|
||||
chunk, (char *)chunk+chunk->size, chunk->size);
|
||||
chunk = chunk->larger;
|
||||
}
|
||||
#endif
|
||||
printf("End of CHUNKS:\n");
|
||||
}
|
||||
|
||||
#else /* Here follows all routines dealing with the SPLAY TREES */
|
||||
|
||||
typedef struct tree_node Tree;
|
||||
struct tree_node {
|
||||
Tree *smaller; /* smaller node */
|
||||
Tree *larger; /* larger node */
|
||||
Tree *same; /* points to a node with identical key */
|
||||
int key; /* the "sort" key */
|
||||
};
|
||||
|
||||
Tree *chunkHead = NULL; /* the root */
|
||||
|
||||
#define compare(i,j) ((i)-(j))
|
||||
|
||||
/* Set this to a key value that will *NEVER* appear otherwise */
|
||||
#define KEY_NOTUSED -1
|
||||
|
||||
/*
|
||||
* Splay using the key i (which may or may not be in the tree.) The starting
|
||||
* root is t. Weight fields are maintained.
|
||||
*/
|
||||
static
|
||||
Tree * splay (int i, Tree *t)
|
||||
{
|
||||
Tree N, *l, *r, *y;
|
||||
int comp;
|
||||
|
||||
if (t == NULL)
|
||||
return t;
|
||||
N.smaller = N.larger = NULL;
|
||||
l = r = &N;
|
||||
|
||||
for (;;) {
|
||||
comp = compare(i, t->key);
|
||||
if (comp < 0) {
|
||||
if (t->smaller == NULL)
|
||||
break;
|
||||
if (compare(i, t->smaller->key) < 0) {
|
||||
y = t->smaller; /* rotate smaller */
|
||||
t->smaller = y->larger;
|
||||
y->larger = t;
|
||||
|
||||
t = y;
|
||||
if (t->smaller == NULL)
|
||||
break;
|
||||
}
|
||||
r->smaller = t; /* link smaller */
|
||||
r = t;
|
||||
t = t->smaller;
|
||||
}
|
||||
else if (comp > 0) {
|
||||
if (t->larger == NULL)
|
||||
break;
|
||||
if (compare(i, t->larger->key) > 0) {
|
||||
y = t->larger; /* rotate larger */
|
||||
t->larger = y->smaller;
|
||||
y->smaller = t;
|
||||
t = y;
|
||||
if (t->larger == NULL)
|
||||
break;
|
||||
}
|
||||
l->larger = t; /* link larger */
|
||||
l = t;
|
||||
t = t->larger;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
l->larger = r->smaller = NULL;
|
||||
|
||||
l->larger = t->smaller; /* assemble */
|
||||
r->smaller = t->larger;
|
||||
t->smaller = N.larger;
|
||||
t->larger = N.smaller;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Insert key i into the tree t. Return a pointer to the resulting tree or
|
||||
NULL if something went wrong. */
|
||||
static
|
||||
Tree *insert(int i, Tree *t, Tree *new)
|
||||
{
|
||||
if (new == NULL) {
|
||||
return t;
|
||||
}
|
||||
|
||||
if (t != NULL) {
|
||||
t = splay(i,t);
|
||||
if (compare(i, t->key)==0) {
|
||||
/* it already exists one of this size */
|
||||
|
||||
new->same = t;
|
||||
new->key = i;
|
||||
new->smaller = t->smaller;
|
||||
new->larger = t->larger;
|
||||
|
||||
t->smaller = new;
|
||||
t->key = KEY_NOTUSED;
|
||||
|
||||
return new; /* new root node */
|
||||
}
|
||||
}
|
||||
|
||||
if (t == NULL) {
|
||||
new->smaller = new->larger = NULL;
|
||||
}
|
||||
else if (compare(i, t->key) < 0) {
|
||||
new->smaller = t->smaller;
|
||||
new->larger = t;
|
||||
t->smaller = NULL;
|
||||
}
|
||||
else {
|
||||
new->larger = t->larger;
|
||||
new->smaller = t;
|
||||
t->larger = NULL;
|
||||
}
|
||||
new->key = i;
|
||||
|
||||
new->same = NULL; /* no identical node (yet) */
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/* Finds and deletes the best-fit node from the tree. Return a pointer to the
|
||||
resulting tree. best-fit means the smallest node that fits the requested
|
||||
size. */
|
||||
static
|
||||
Tree *removebestfit(int i, Tree *t, Tree **removed)
|
||||
{
|
||||
Tree *x;
|
||||
|
||||
if (t==NULL)
|
||||
return NULL;
|
||||
t = splay(i,t);
|
||||
if(compare(i, t->key) > 0) {
|
||||
/* too small node, try the larger chain */
|
||||
if(t->larger)
|
||||
t=splay(t->larger->key, t);
|
||||
else {
|
||||
/* fail */
|
||||
*removed = NULL;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
if (compare(i, t->key) <= 0) { /* found it */
|
||||
|
||||
/* FIRST! Check if there is a list with identical sizes */
|
||||
x = t->same;
|
||||
if(x) {
|
||||
/* there is, pick one from the list */
|
||||
|
||||
/* 'x' is the new root node */
|
||||
|
||||
x->key = t->key;
|
||||
x->larger = t->larger;
|
||||
x->smaller = t->smaller;
|
||||
*removed = t;
|
||||
return x; /* new root */
|
||||
}
|
||||
|
||||
if (t->smaller == NULL) {
|
||||
x = t->larger;
|
||||
}
|
||||
else {
|
||||
x = splay(i, t->smaller);
|
||||
x->larger = t->larger;
|
||||
}
|
||||
*removed = t;
|
||||
|
||||
return x;
|
||||
}
|
||||
else {
|
||||
*removed = NULL; /* no match */
|
||||
return t; /* It wasn't there */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Deletes the node we point out from the tree if it's there. Return a pointer
|
||||
to the resulting tree. */
|
||||
static
|
||||
Tree *removebyaddr(Tree *t, Tree *remove)
|
||||
{
|
||||
Tree *x;
|
||||
|
||||
if (!t || !remove)
|
||||
return NULL;
|
||||
|
||||
if(KEY_NOTUSED == remove->key) {
|
||||
/* just unlink ourselves nice and quickly: */
|
||||
remove->smaller->same = remove->same;
|
||||
if(remove->same)
|
||||
remove->same->smaller = remove->smaller;
|
||||
/* voila, we're done! */
|
||||
return t;
|
||||
}
|
||||
|
||||
t = splay(remove->key,t);
|
||||
|
||||
/* Check if there is a list with identical sizes */
|
||||
|
||||
x = t->same;
|
||||
if(x) {
|
||||
/* 'x' is the new root node */
|
||||
|
||||
x->key = t->key;
|
||||
x->larger = t->larger;
|
||||
x->smaller = t->smaller;
|
||||
|
||||
return x; /* new root */
|
||||
}
|
||||
|
||||
/* Remove the actualy root node: */
|
||||
|
||||
if (t->smaller == NULL) {
|
||||
x = t->larger;
|
||||
}
|
||||
else {
|
||||
x = splay(remove->key, t->smaller);
|
||||
x->larger = t->larger;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MALLOC
|
||||
static
|
||||
int printtree(Tree * t, int d, char output)
|
||||
{
|
||||
int distance=0;
|
||||
Tree *node;
|
||||
int i;
|
||||
if (t == NULL)
|
||||
return 0;
|
||||
distance += printtree(t->larger, d+1, output);
|
||||
for (i=0; i<d; i++)
|
||||
if(output)
|
||||
printf(" ");
|
||||
|
||||
if(output) {
|
||||
printf("%d[%d]", t->key, i);
|
||||
}
|
||||
|
||||
for(node = t->same; node; node = node->same) {
|
||||
distance += i; /* this has the same "virtual" distance */
|
||||
|
||||
if(output)
|
||||
printf(" [+]");
|
||||
}
|
||||
if(output)
|
||||
puts("");
|
||||
|
||||
distance += i;
|
||||
distance += printtree(t->smaller, d+1, output);
|
||||
return distance;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Here follow the look-alike interface so that the tree-function names are
|
||||
the same as the list-ones to enable easy interchange */
|
||||
|
||||
void bmalloc_remove_chunksize(void *data)
|
||||
{
|
||||
chunkHead = removebyaddr(chunkHead, data);
|
||||
}
|
||||
|
||||
void bmalloc_insert_bysize(char *data, size_t size)
|
||||
{
|
||||
chunkHead = insert(size, chunkHead, (Tree *)data);
|
||||
}
|
||||
|
||||
char *bmalloc_obtainbysize( size_t size)
|
||||
{
|
||||
Tree *receive;
|
||||
chunkHead = removebestfit(size, chunkHead, &receive);
|
||||
return (char *)receive;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MALLOC
|
||||
void bmalloc_print_sizes(void)
|
||||
{
|
||||
printtree(chunkHead, 0, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by Daniel Stenberg
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
void bmalloc_remove_chunksize(void *data);
|
||||
void bmalloc_insert_bysize(char *data, size_t size);
|
||||
char *bmalloc_obtainbysize( size_t size);
|
||||
#ifdef DEBUG_MALLOC
|
||||
void bmalloc_print_sizes(void);
|
||||
#endif
|
||||
|
|
@ -1,634 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by Daniel Stenberg
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Dynamic small-blocks Memory Allocation
|
||||
*
|
||||
* Author: Daniel Stenberg <daniel@haxx.se>
|
||||
*
|
||||
* Read THOUGHTS for theories and details on the implementation.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h> /* memcpy */
|
||||
|
||||
#ifdef DEBUG_MALLOC
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
#ifdef PSOS
|
||||
#include <psos.h>
|
||||
#define SEMAPHORE /* the PSOS routines use semaphore protection */
|
||||
#else
|
||||
|
||||
#endif
|
||||
|
||||
#define BMALLOC /* we use our own big-malloc system */
|
||||
|
||||
#ifdef BMALLOC
|
||||
#include "bmalloc.h"
|
||||
#endif
|
||||
|
||||
/* Each TOP takes care of a chain of BLOCKS */
|
||||
struct MemTop {
|
||||
struct MemBlock *chain; /* pointer to the BLOCK chain */
|
||||
long nfree; /* total number of free FRAGMENTS in the chain */
|
||||
short nmax; /* total number of FRAGMENTS in this kind of BLOCK */
|
||||
size_t fragsize; /* the size of each FRAGMENT */
|
||||
|
||||
#ifdef SEMAPHORE /* if we're protecting the list with SEMAPHORES */
|
||||
long semaphore_id; /* semaphore used to lock this particular list */
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
/* Each BLOCK takes care of an amount of FRAGMENTS */
|
||||
struct MemBlock {
|
||||
struct MemTop *top; /* our TOP struct */
|
||||
struct MemBlock *next; /* next BLOCK */
|
||||
struct MemBlock *prev; /* prev BLOCK */
|
||||
|
||||
struct MemFrag *first; /* the first free FRAGMENT in this block */
|
||||
|
||||
short nfree; /* number of free FRAGMENTS in this BLOCK */
|
||||
};
|
||||
|
||||
/* This is the data kept in all _free_ FRAGMENTS */
|
||||
struct MemFrag {
|
||||
struct MemFrag *next; /* next free FRAGMENT */
|
||||
struct MemFrag *prev; /* prev free FRAGMENT */
|
||||
};
|
||||
|
||||
/* This is the data kept in all _allocated_ FRAGMENTS and BLOCKS. We add this
|
||||
to the allocation size first thing in the ALLOC function to make room for
|
||||
this smoothly. */
|
||||
|
||||
struct MemInfo {
|
||||
void *block;
|
||||
/* which BLOCK is our father, if BLOCK_BIT is set it means this is a
|
||||
stand-alone, large allocation and then the rest of the bits should be
|
||||
treated as the size of the block */
|
||||
#define BLOCK_BIT 1
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Defines */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
#define MEMINCR(addr,x) memchange(addr, x)
|
||||
#define MEMDECR(addr,x) memchange(addr,-(x))
|
||||
#else
|
||||
#define MEMINCR(a,x)
|
||||
#define MEMDECR(a,x)
|
||||
#endif
|
||||
|
||||
/* The low level functions used to get memory from the OS and to return memory
|
||||
to the OS, we may also define a stub that does the actual allocation and
|
||||
free, these are the defined function names used in the dmalloc system
|
||||
anyway: */
|
||||
#ifdef PSOS
|
||||
|
||||
#ifdef DEBUG_MALLOC
|
||||
#define DMEM_OSALLOCMEM(size,pointer,type) pointer=(type)dbgmalloc(size)
|
||||
#define DMEM_OSFREEMEM(x) dbgfree(x)
|
||||
#else
|
||||
#define DMEM_OSALLOCMEM(size,pointer,type) rn_getseg(0,size,RN_NOWAIT,0,(void **)&pointer)
|
||||
/* Similar, but this returns the memory */
|
||||
#define DMEM_OSFREEMEM(x) rn_retseg(0, x)
|
||||
#endif
|
||||
|
||||
/* Argument: <id> */
|
||||
#define SEMAPHOREOBTAIN(x) sm_p(x, SM_WAIT, 0)
|
||||
/* Argument: <id> */
|
||||
#define SEMAPHORERETURN(x) sm_v(x)
|
||||
/* Argument: <name> <id-variable name> */
|
||||
#define SEMAPHORECREATE(x,y) sm_create(x, 1, SM_FIFO, (ULONG *)&(y))
|
||||
|
||||
#else
|
||||
#ifdef BMALLOC /* use our own big-memory-allocation system */
|
||||
#define DMEM_OSALLOCMEM(size,pointer,type) pointer=(type)bmalloc(size)
|
||||
#define DMEM_OSFREEMEM(x) bfree(x)
|
||||
#elif DEBUG_MALLOC
|
||||
#define DMEM_OSALLOCMEM(size,pointer,type) pointer=(type)dbgmalloc(size)
|
||||
#define DMEM_OSFREEMEM(x) dbgfree(x)
|
||||
#else
|
||||
#define DMEM_OSALLOCMEM(size,pointer,type) pointer=(type)malloc(size)
|
||||
#define DMEM_OSFREEMEM(x) free(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* the largest memory allocation that is made a FRAGMENT: (grab the highest
|
||||
number from the list below) */
|
||||
#define DMEM_LARGESTSIZE 2032
|
||||
|
||||
/* The total size of a BLOCK used for FRAGMENTS
|
||||
In order to make this use only *1* even alignment from the big-block-
|
||||
allocation-system (possible the bmalloc() system also written by me)
|
||||
we need to subtract the [maximum] struct sizes that could get added all
|
||||
the way through to the grab from the memory. */
|
||||
#define DMEM_BLOCKSIZE 4064 /* (4096 - sizeof(struct MemBlock) - 12) */
|
||||
|
||||
/* Since the blocksize isn't an even 2^X story anymore, we make a table with
|
||||
the FRAGMENT sizes and amounts that fills up a BLOCK nicely */
|
||||
|
||||
/* a little 'bc' script that helps us maximize the usage:
|
||||
- for 32-bit aligned addresses (SPARC crashes otherwise):
|
||||
for(i=20; i<2040; i+=4) { a=4064/i; if(a*i >= 4060) { {i;} } }
|
||||
|
||||
I try to approximate a double of each size, starting with ~20. We don't do
|
||||
ODD sizes since several CPU flavours dump core when accessing such
|
||||
addresses. We try to do 32-bit aligned to make ALL kinds of CPUs to remain
|
||||
happy with us.
|
||||
*/
|
||||
|
||||
static const unsigned short qinfo[]= {
|
||||
20, 28, 52, 116, 312, 580, 1016, 2032
|
||||
};
|
||||
|
||||
#define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Globals */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/* keeper of the chain of BLOCKS */
|
||||
static struct MemTop top[ sizeof(qinfo)/sizeof(qinfo[0]) ];
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Start of the real code */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
#ifdef DEBUG_MALLOC
|
||||
/************
|
||||
* A few functions that are verbose and tells us about the current status
|
||||
* of the dmalloc system
|
||||
***********/
|
||||
|
||||
void dmalloc_status(void)
|
||||
{
|
||||
unsigned int i;
|
||||
int used;
|
||||
int num;
|
||||
int totalfree=0;
|
||||
struct MemBlock *block;
|
||||
for(i=0; i<sizeof(qinfo)/sizeof(qinfo[0]);i++) {
|
||||
block = top[i].chain;
|
||||
used = 0;
|
||||
num = 0;
|
||||
while(block) {
|
||||
used += top[i].nmax-block->nfree;
|
||||
num++;
|
||||
block = block->next;
|
||||
}
|
||||
printf("Q %d (FRAG %4d), USED %4d FREE %4ld (SIZE %4ld) BLOCKS %d\n",
|
||||
i, top[i].fragsize, used, top[i].nfree,
|
||||
top[i].nfree*top[i].fragsize, num);
|
||||
totalfree += top[i].nfree*top[i].fragsize;
|
||||
}
|
||||
printf("Total unused memory stolen by dmalloc: %d\n", totalfree);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
static void dmalloc_failed(size_t size)
|
||||
{
|
||||
printf("*** " __FILE__ " Couldn't allocate %d bytes\n", size);
|
||||
dmalloc_status();
|
||||
}
|
||||
#else
|
||||
#define dmalloc_failed(x)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
|
||||
#define DBG(x) syslog x
|
||||
|
||||
void syslog(char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stdout, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void memchange(void *a, int x)
|
||||
{
|
||||
static int memory=0;
|
||||
static int count=0;
|
||||
static int max=0;
|
||||
if(memory > max)
|
||||
max = memory;
|
||||
memory += x;
|
||||
DBG(("%d. PTR %p / %d TOTAL %d MAX %d\n", ++count, a, x, memory, max));
|
||||
}
|
||||
#else
|
||||
#define DBG(x)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* FragBlock()
|
||||
*
|
||||
* This function makes FRAGMENTS of the BLOCK sent as argument.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
static void FragBlock(char *memp, int size)
|
||||
{
|
||||
struct MemFrag *frag=(struct MemFrag *)memp;
|
||||
struct MemFrag *prev=NULL; /* no previous in the first round */
|
||||
int count=0;
|
||||
while((count+size) <= DMEM_BLOCKSIZE) {
|
||||
frag->next = (struct MemFrag *)((char *)frag + size);
|
||||
frag->prev = prev;
|
||||
prev = frag;
|
||||
(char *)frag += size;
|
||||
count += size;
|
||||
}
|
||||
prev->next = NULL; /* the last one has no next struct */
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* dmalloc_initialize();
|
||||
*
|
||||
* Call before the first dmalloc(). Inits a few memory things.
|
||||
*
|
||||
**************************************************************************/
|
||||
void dmalloc_initialize(void)
|
||||
{
|
||||
unsigned int i;
|
||||
/* Setup the nmax and fragsize fields of the top structs */
|
||||
for(i=0; i< sizeof(qinfo)/sizeof(qinfo[0]); i++) {
|
||||
top[i].fragsize = qinfo[i];
|
||||
top[i].nmax = DMEM_BLOCKSIZE/qinfo[i];
|
||||
|
||||
#ifdef PSOS
|
||||
/* for some reason, these aren't nulled from start: */
|
||||
top[i].chain = NULL; /* no BLOCKS */
|
||||
top[i].nfree = 0; /* no FRAGMENTS */
|
||||
#endif
|
||||
#ifdef SEMAPHORE
|
||||
{
|
||||
char name[7];
|
||||
snprintf(name, 7, "MEM%d", i);
|
||||
SEMAPHORECREATE(name, top[i].semaphore_id);
|
||||
/* doesn't matter if it failed, we continue anyway ;-( */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* fragfromblock()
|
||||
*
|
||||
* This should return a fragment from the block and mark it as used
|
||||
* accordingly.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
static void *fragfromblock(struct MemBlock *block)
|
||||
{
|
||||
/* make frag point to the first free FRAGMENT */
|
||||
struct MemFrag *frag = block->first;
|
||||
struct MemInfo *mem = (struct MemInfo *)frag;
|
||||
|
||||
/*
|
||||
* Remove the FRAGMENT from the list and decrease the free counters.
|
||||
*/
|
||||
block->first = frag->next; /* new first free FRAGMENT */
|
||||
|
||||
block->nfree--; /* BLOCK counter */
|
||||
block->top->nfree--; /* TOP counter */
|
||||
|
||||
/* heal the FRAGMENT list */
|
||||
if(frag->prev) {
|
||||
frag->prev->next = frag->next;
|
||||
}
|
||||
if(frag->next) {
|
||||
frag->next->prev = frag->prev;
|
||||
}
|
||||
mem->block = block; /* no block bit set here */
|
||||
|
||||
return ((char *)mem)+sizeof(struct MemInfo);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* dmalloc()
|
||||
*
|
||||
* This needs no explanation. A malloc() look-alike.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
void *malloc(size_t size)
|
||||
{
|
||||
void *mem;
|
||||
|
||||
DBG(("malloc(%d)\n", size));
|
||||
|
||||
/* First, we make room for the space needed in every allocation */
|
||||
size += sizeof(struct MemInfo);
|
||||
|
||||
if(size < DMEM_LARGESTSIZE) {
|
||||
/* get a FRAGMENT */
|
||||
|
||||
struct MemBlock *block=NULL; /* SAFE */
|
||||
struct MemBlock *newblock=NULL; /* SAFE */
|
||||
struct MemTop *memtop=NULL; /* SAFE */
|
||||
|
||||
/* Determine which queue to use */
|
||||
unsigned int queue;
|
||||
for(queue=0; size > qinfo[queue]; queue++)
|
||||
;
|
||||
do {
|
||||
/* This is the head master of our chain: */
|
||||
memtop = &top[queue];
|
||||
|
||||
DBG(("Top info: CHAIN %p FREE %d MAX %d FRAGSIZE %d\n",
|
||||
memtop->chain,
|
||||
memtop->nfree,
|
||||
memtop->nmax,
|
||||
memtop->fragsize));
|
||||
|
||||
#ifdef SEMAPHORE
|
||||
if(SEMAPHOREOBTAIN(memtop->semaphore_id))
|
||||
return NULL; /* failed somehow */
|
||||
#endif
|
||||
|
||||
/* get the first BLOCK in the chain */
|
||||
block = memtop->chain;
|
||||
|
||||
/* check if we have a free FRAGMENT */
|
||||
if(memtop->nfree) {
|
||||
/* there exists a free FRAGMENT in this chain */
|
||||
|
||||
/**** We'd prefer to not have this loop here! ****/
|
||||
|
||||
/* search for the free FRAGMENT */
|
||||
while(!block->nfree)
|
||||
block = block->next; /* check next BLOCK */
|
||||
|
||||
/*
|
||||
* Now 'block' is the first BLOCK with a free FRAGMENT
|
||||
*/
|
||||
|
||||
mem = fragfromblock(block);
|
||||
|
||||
}
|
||||
else {
|
||||
/* we do *not* have a free FRAGMENT but need to get us a new
|
||||
* BLOCK */
|
||||
|
||||
DMEM_OSALLOCMEM(DMEM_BLOCKSIZE + sizeof(struct MemBlock),
|
||||
newblock,
|
||||
struct MemBlock *);
|
||||
if(!newblock) {
|
||||
if(++queue < sizeof(qinfo)/sizeof(qinfo[0])) {
|
||||
/* There are queues for bigger FRAGMENTS that we
|
||||
* should check before we fail this for real */
|
||||
#ifdef DEBUG_VERBOSE
|
||||
printf("*** " __FILE__ " Trying a bigger Q: %d\n",
|
||||
queue);
|
||||
#endif
|
||||
mem = NULL;
|
||||
}
|
||||
else {
|
||||
dmalloc_failed(size- sizeof(struct MemInfo));
|
||||
return NULL; /* not enough memory */
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* allocation of big BLOCK was successful */
|
||||
MEMINCR(newblock, DMEM_BLOCKSIZE +
|
||||
sizeof(struct MemBlock));
|
||||
|
||||
memtop->chain = newblock; /* attach this BLOCK to the
|
||||
chain */
|
||||
newblock->next = block; /* point to the previous first
|
||||
BLOCK */
|
||||
if(block)
|
||||
block->prev = newblock; /* point back on this new
|
||||
BLOCK */
|
||||
newblock->prev = NULL; /* no previous */
|
||||
newblock->top = memtop; /* our head master */
|
||||
|
||||
/* point to the new first FRAGMENT */
|
||||
newblock->first = (struct MemFrag *)
|
||||
((char *)newblock+sizeof(struct MemBlock));
|
||||
|
||||
/* create FRAGMENTS of the BLOCK: */
|
||||
FragBlock((char *)newblock->first, memtop->fragsize);
|
||||
|
||||
/* fix the nfree counters */
|
||||
newblock->nfree = memtop->nmax;
|
||||
memtop->nfree += memtop->nmax;
|
||||
|
||||
/* get a FRAGMENT from the BLOCK */
|
||||
mem = fragfromblock(newblock);
|
||||
}
|
||||
}
|
||||
#ifdef SEMAPHORE
|
||||
SEMAPHORERETURN(memtop->semaphore_id); /* let it go */
|
||||
#endif
|
||||
} while(NULL == mem); /* if we should retry a larger FRAGMENT */
|
||||
}
|
||||
else {
|
||||
/* get a stand-alone BLOCK */
|
||||
struct MemInfo *meminfo;
|
||||
|
||||
if(size&1)
|
||||
/* don't leave this with an odd size since we'll use that bit for
|
||||
information */
|
||||
size++;
|
||||
|
||||
DMEM_OSALLOCMEM(size, meminfo, struct MemInfo *);
|
||||
|
||||
if(meminfo) {
|
||||
MEMINCR(meminfo, size);
|
||||
meminfo->block = (void *)(size|BLOCK_BIT);
|
||||
mem = (char *)meminfo + sizeof(struct MemInfo);
|
||||
}
|
||||
else {
|
||||
dmalloc_failed(size);
|
||||
mem = NULL;
|
||||
}
|
||||
}
|
||||
return (void *)mem;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* dfree()
|
||||
*
|
||||
* This needs no explanation. A free() look-alike.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
void free(void *memp)
|
||||
{
|
||||
struct MemInfo *meminfo = (struct MemInfo *)
|
||||
((char *)memp- sizeof(struct MemInfo));
|
||||
|
||||
DBG(("free(%p)\n", memp));
|
||||
|
||||
if(!((size_t)meminfo->block&BLOCK_BIT)) {
|
||||
/* this is a FRAGMENT we have to deal with */
|
||||
|
||||
struct MemBlock *block=meminfo->block;
|
||||
struct MemTop *memtop = block->top;
|
||||
|
||||
#ifdef SEMAPHORE
|
||||
SEMAPHOREOBTAIN(memtop->semaphore_id);
|
||||
#endif
|
||||
|
||||
/* increase counters */
|
||||
block->nfree++;
|
||||
memtop->nfree++;
|
||||
|
||||
/* is this BLOCK completely empty now? */
|
||||
if(block->nfree == memtop->nmax) {
|
||||
/* yes, return the BLOCK to the system */
|
||||
if(block->prev)
|
||||
block->prev->next = block->next;
|
||||
else
|
||||
memtop->chain = block->next;
|
||||
if(block->next)
|
||||
block->next->prev = block->prev;
|
||||
|
||||
memtop->nfree -= memtop->nmax; /* total counter subtraction */
|
||||
MEMDECR(block, DMEM_BLOCKSIZE + sizeof(struct MemBlock));
|
||||
DMEM_OSFREEMEM((void *)block); /* return the whole block */
|
||||
}
|
||||
else {
|
||||
/* there are still used FRAGMENTS in the BLOCK, link this one
|
||||
into the chain of free ones */
|
||||
struct MemFrag *frag = (struct MemFrag *)meminfo;
|
||||
frag->prev = NULL;
|
||||
frag->next = block->first;
|
||||
if(block->first)
|
||||
block->first->prev = frag;
|
||||
block->first = frag;
|
||||
}
|
||||
#ifdef SEMAPHORE
|
||||
SEMAPHORERETURN(memtop->semaphore_id);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
/* big stand-alone block, just give it back to the OS: */
|
||||
|
||||
/* clean BLOCK_BIT */
|
||||
MEMDECR(meminfo->block, (size_t)meminfo->block&~BLOCK_BIT);
|
||||
DMEM_OSFREEMEM((void *)meminfo);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* drealloc()
|
||||
*
|
||||
* This needs no explanation. A realloc() look-alike.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
void *realloc(void *ptr, size_t size)
|
||||
{
|
||||
struct MemInfo *meminfo = (struct MemInfo *)
|
||||
((char *)ptr- sizeof(struct MemInfo));
|
||||
/*
|
||||
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
* NOTE: the ->size field of the meminfo will now contain the MemInfo
|
||||
* struct size too!
|
||||
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
*/
|
||||
void *mem=NULL; /* SAFE */
|
||||
size_t prevsize;
|
||||
|
||||
/* NOTE that this is only valid if BLOCK_BIT isn't set: */
|
||||
struct MemBlock *block;
|
||||
|
||||
DBG(("realloc(%p, %d)\n", ptr, size));
|
||||
|
||||
if(NULL == ptr)
|
||||
return malloc( size );
|
||||
|
||||
block = meminfo->block;
|
||||
|
||||
/* Here we check if this is a FRAGMENT and if the new size is
|
||||
still smaller than the fragsize for this block. */
|
||||
if(!((size_t)block&BLOCK_BIT) &&
|
||||
(size + sizeof(struct MemInfo) < block->top->fragsize )) {
|
||||
|
||||
prevsize = block->top->fragsize;
|
||||
/* This is a FRAGMENT and new size is possible to retain within the
|
||||
same FRAGMENT */
|
||||
if((prevsize > qinfo[0]) &&
|
||||
/* this is not the smallest memory Q */
|
||||
(size < (block->top-1)->fragsize))
|
||||
/* This fits in a smaller fragment, so we will make a realloc
|
||||
here */
|
||||
;
|
||||
else
|
||||
mem = ptr; /* Just return the same pointer as we got in. */
|
||||
}
|
||||
if(!mem) {
|
||||
if((size_t)meminfo->block&BLOCK_BIT) {
|
||||
/* This is a stand-alone BLOCK */
|
||||
prevsize = ((size_t)meminfo->block&~BLOCK_BIT) -
|
||||
sizeof(struct MemInfo);
|
||||
}
|
||||
else
|
||||
/* a FRAGMENT realloc that no longer fits within the same FRAGMENT
|
||||
* or one that fits in a smaller */
|
||||
prevsize = block->top->fragsize;
|
||||
|
||||
/* No tricks involved here, just grab a new bite of memory, copy the
|
||||
* data from the old place and free the old memory again. */
|
||||
mem = malloc(size);
|
||||
if(mem) {
|
||||
memcpy(mem, ptr, MIN(size, prevsize) );
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* dcalloc()
|
||||
*
|
||||
* This needs no explanation. A calloc() look-alike.
|
||||
*
|
||||
**************************************************************************/
|
||||
/* Allocate an array of NMEMB elements each SIZE bytes long.
|
||||
The entire array is initialized to zeros. */
|
||||
void *
|
||||
calloc (size_t nmemb, size_t size)
|
||||
{
|
||||
void *result = malloc (nmemb * size);
|
||||
|
||||
if (result != NULL)
|
||||
memset (result, 0, nmemb * size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by Daniel Stenberg
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef _DMALLOC_H_
|
||||
#define _DMALLOC_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void *malloc(size_t);
|
||||
void *calloc (size_t nmemb, size_t size);
|
||||
void free(void *);
|
||||
void *realloc(void *, size_t);
|
||||
|
||||
/* use this to intialize the internals of the dmalloc engine */
|
||||
void dmalloc_initialize(void);
|
||||
|
||||
#ifdef DEBUG
|
||||
void dmalloc_status(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,844 +0,0 @@
|
|||
/*
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// __________ __ ___. //
|
||||
// Open \______ \ ____ ____ | | _\_ |__ _______ ___ //
|
||||
// Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / //
|
||||
// Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < //
|
||||
// Software |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ //
|
||||
// \/ \/ \/ \/ \/ //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
/////////////////////////////////////
|
||||
// Copyright (C) 2002 by Alan Korr //
|
||||
/////////////////////////////////////
|
||||
//
|
||||
// All files in this archive are subject to the GNU General Public License.
|
||||
// See the file COPYING in the source tree root for full license agreement.
|
||||
//
|
||||
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
|
||||
// either express or implied.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#define MEMORY_PAGE_USE_SPLAY_TREE
|
||||
|
||||
/*
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// MEMORY PAGE //
|
||||
/////////////////
|
||||
//
|
||||
//
|
||||
*/
|
||||
|
||||
struct __memory_free_page
|
||||
{
|
||||
struct __memory_free_page *less,*more;
|
||||
char reserved[MEMORY_PAGE_MINIMAL_SIZE - 2*sizeof (struct memory_free_page *)];
|
||||
};
|
||||
|
||||
#define LESS -1
|
||||
#define MORE +1
|
||||
|
||||
extern struct __memory_free_page __memory_free_page[MEMORY_TOTAL_PAGES] asm("dram");
|
||||
|
||||
char __memory_free_page_order[MEMORY_TOTAL_PAGES];
|
||||
struct __memory_free_page *__memory_free_page_bin[MEMORY_TOTAL_ORDERS];
|
||||
|
||||
static inline unsigned int __memory_get_size (int order)
|
||||
/*
|
||||
SH1 has very poor shift instructions (only <<1,>>1,<<2,>>2,<<8,
|
||||
>>8,<<16 and >>16), so we should use a lookup table to speedup.
|
||||
*/
|
||||
{
|
||||
return
|
||||
(
|
||||
(unsigned short [MEMORY_TOTAL_ORDERS])
|
||||
{
|
||||
1<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
2<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
4<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
8<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
16<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
32<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
64<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
128<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
256<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
512<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
1024<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
2048<<MEMORY_PAGE_MINIMAL_ORDER,
|
||||
4096<<MEMORY_PAGE_MINIMAL_ORDER
|
||||
}
|
||||
)[order];
|
||||
}
|
||||
|
||||
static inline struct __memory_free_page *__memory_get_neighbour (struct __memory_free_page *node,unsigned int size)
|
||||
{
|
||||
return ((struct __memory_free_page *)((unsigned)node ^ size));
|
||||
}
|
||||
|
||||
static inline int __memory_get_order (struct __memory_free_page *node)
|
||||
{
|
||||
return __memory_free_page_order[node - __memory_free_page];
|
||||
}
|
||||
|
||||
static inline void __memory_set_order (struct __memory_free_page *node,int order)
|
||||
{
|
||||
__memory_free_page_order[node - __memory_free_page] = order;
|
||||
}
|
||||
|
||||
#ifdef MEMORY_PAGE_USE_SPLAY_TREE
|
||||
|
||||
static struct __memory_free_page *__memory_splay_page (struct __memory_free_page *root,struct __memory_free_page *node)
|
||||
{
|
||||
struct __memory_free_page *down;
|
||||
struct __memory_free_page *less;
|
||||
struct __memory_free_page *more;
|
||||
struct __memory_free_page *head[2];
|
||||
((struct __memory_free_page *)head)->less =
|
||||
((struct __memory_free_page *)head)->more = 0;
|
||||
less =
|
||||
more = &head;
|
||||
while (1)
|
||||
{
|
||||
if (node < root)
|
||||
{
|
||||
if ((down = root->less))
|
||||
{
|
||||
if (node < down)
|
||||
{
|
||||
root->less = down->more;
|
||||
down->more = root;
|
||||
root = down;
|
||||
if (!root->less)
|
||||
break;
|
||||
}
|
||||
more->less = root;
|
||||
more = root;
|
||||
root = root->less;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (root < node)
|
||||
{
|
||||
if ((down = root->more))
|
||||
{
|
||||
if (root < node)
|
||||
{
|
||||
root->more = down->less;
|
||||
down->less = root;
|
||||
root = down;
|
||||
if (!root->more)
|
||||
break;
|
||||
}
|
||||
less->more = root;
|
||||
less = root;
|
||||
root = root->more;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
less->more = root->less;
|
||||
more->less = root->more;
|
||||
root->less = ((struct __memory_free_page *)head)->more;
|
||||
root->more = ((struct __memory_free_page *)head)->less;
|
||||
return root;
|
||||
}
|
||||
|
||||
static inline void __memory_insert_page (int order,struct __memory_free_page *node)
|
||||
{
|
||||
struct __memory_free_page *root = __memory_free_page_bin[order];
|
||||
if (!root)
|
||||
{
|
||||
node->less =
|
||||
node->more = 0;
|
||||
}
|
||||
else if (node < (root = __memory_splay_page (root,node)))
|
||||
{
|
||||
node->less = root->less;
|
||||
node->more = root;
|
||||
root->less = 0;
|
||||
}
|
||||
else if (node > root)
|
||||
{
|
||||
node->less = root;
|
||||
node->more = root->more;
|
||||
node->more = 0;
|
||||
}
|
||||
__memory_free_page_bin[order] = node;
|
||||
__memory_set_order (node,order);
|
||||
return;
|
||||
}
|
||||
|
||||
static inline struct __memory_free_page *__memory_pop_page (int order,int want)
|
||||
{
|
||||
struct __memory_free_page *root = __memory_free_page_bin[order];
|
||||
if (root)
|
||||
{
|
||||
root = __memory_splay_page (root,__memory_free_page);
|
||||
__memory_free_page_bin[order] = root->more;
|
||||
__memory_set_order (root,~want);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
static inline void __memory_remove_page (int order,struct __memory_free_page *node)
|
||||
{
|
||||
struct __memory_free_page *root = __memory_free_page_bin[order];
|
||||
root = __memory_splay_page (root,node);
|
||||
if (root->less)
|
||||
{
|
||||
node = __memory_splay_page (root->less,node);
|
||||
node->more = root->more;
|
||||
}
|
||||
else
|
||||
node = root->more;
|
||||
__memory_free_page_bin[order] = node;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void __memory_insert_page (int order,struct __memory_free_page *node)
|
||||
{
|
||||
struct __memory_free_page *head = __memory_free_page_bin[order];
|
||||
node->less = 0;
|
||||
node->more = head;
|
||||
if (head)
|
||||
head->less = node;
|
||||
__memory_free_page_bin[order] = node;
|
||||
__memory_set_order (node,order);
|
||||
}
|
||||
|
||||
static inline struct __memory_free_page *pop_page (int order,int want)
|
||||
{
|
||||
struct __memory_free_page *node = __memory_free_page_bin[order];
|
||||
if (node)
|
||||
{
|
||||
__memory_free_page_bin[order] = node->more;
|
||||
if (node->more)
|
||||
node->more->less = 0;
|
||||
__memory_set_order (node,~want);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline void __memory_remove_page (int order,struct __memory_free_page *node)
|
||||
{
|
||||
if (node->less)
|
||||
node->less->more = node->more;
|
||||
else
|
||||
__memory_free_page_bin[order] = node->more;
|
||||
if (node->more)
|
||||
node->more->less = node->less;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline void __memory_push_page (int order,struct __memory_free_page *node)
|
||||
{
|
||||
node->less = 0;
|
||||
node->more = 0;
|
||||
__memory_free_page_bin[order] = node;
|
||||
__memory_set_order (node,order);
|
||||
}
|
||||
|
||||
static struct __memory_free_page *__memory_allocate_page (unsigned int size,int order)
|
||||
{
|
||||
struct __memory_free_page *node;
|
||||
int min = order;
|
||||
while ((unsigned)order <= (MEMORY_TOTAL_ORDERS - 1))
|
||||
/* order is valid ? */
|
||||
{
|
||||
if (!(node = __memory_pop_page (order,min)))
|
||||
/* no free page of this order ? */
|
||||
{
|
||||
++order; size <<= 1;
|
||||
continue;
|
||||
}
|
||||
while (order > min)
|
||||
/* split our larger page in smaller pages */
|
||||
{
|
||||
--order; size >>= 1;
|
||||
__memory_push_page (order,(struct __memory_free_page *)((unsigned int)node + size));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
static inline void __memory_release_page (struct __memory_free_page *node,unsigned int size,int order)
|
||||
{
|
||||
struct __memory_free_page *neighbour;
|
||||
while ((order <= (MEMORY_TOTAL_ORDERS - 1)) &&
|
||||
((neighbour = __memory_get_neighbour (node,size)),
|
||||
(__memory_get_order (neighbour) == order)))
|
||||
/* merge our released page with its contiguous page into a larger page */
|
||||
{
|
||||
__memory_remove_page (order,neighbour);
|
||||
++order; size <<= 1;
|
||||
if (neighbour < node)
|
||||
node = neighbour;
|
||||
}
|
||||
__memory_insert_page (order,node);
|
||||
}
|
||||
|
||||
void *memory_page_allocate (int order)
|
||||
{
|
||||
if ((unsigned)order < MEMORY_TOTAL_ORDERS)
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
return __memory_allocate_page (__memory_get_size (order),order);
|
||||
}
|
||||
|
||||
int memory_page_release (void *address)
|
||||
{
|
||||
struct __memory_free_page *node = (struct __memory_free_page *)address;
|
||||
int order = ~__memory_get_order (node);
|
||||
if ((unsigned)order < MEMORY_TOTAL_ORDERS)
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
__memory_release_page (node,__memory_get_size (order),order);
|
||||
return MEMORY_RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
void memory_page_release_range (unsigned int start,unsigned int end)
|
||||
{
|
||||
start = ((start + MEMORY_PAGE_MINIMAL_SIZE - 1) & -MEMORY_PAGE_MINIMAL_SIZE;
|
||||
end = ((end ) & -MEMORY_PAGE_MINIMAL_SIZE;
|
||||
/* release pages between _start_ and _end_ (each must be 512 bytes long) */
|
||||
for (; start < end; start += MEMORY_PAGE_MINIMAL_SIZE)
|
||||
memory_page_release (start);
|
||||
}
|
||||
|
||||
static inline void __memory_page_setup (void)
|
||||
{
|
||||
#if 0
|
||||
memory_set (__memory_free_page_bin,0,MEMORY_TOTAL_ORDERS *sizeof (struct memory_free_page *));
|
||||
#endif
|
||||
/* all pages are marked as used (no free page) */
|
||||
memory_set (__memory_free_page_order,~0,MEMORY_TOTAL_PAGES);
|
||||
}
|
||||
|
||||
/*
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// MEMORY CACHE //
|
||||
//////////////////
|
||||
//
|
||||
//
|
||||
*/
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
#define MEMORY_MAX_PAGE_ORDER_PER_SLAB (5)
|
||||
#define MEMORY_MAX_PAGE_SIZE_PER_SLAB (MEMORY_PAGE_MINIMAL_SIZE << MEMORY_MAX_PAGE_ORDER_PER_SLAB)
|
||||
#define MEMORY_MIN_BLOCKS_PER_SLAB (4)
|
||||
|
||||
struct __memory_free_block
|
||||
{
|
||||
struct __memory_free_block *link;
|
||||
};
|
||||
|
||||
struct __memory_slab
|
||||
{
|
||||
struct __memory_slab *less,*more;
|
||||
unsigned int free_blocks_left;
|
||||
struct __memory_free_block *free_block_list;
|
||||
};
|
||||
|
||||
#define WITH_NO_FREE_BLOCK 0
|
||||
#define WITH_SOME_FREE_BLOCKS 1
|
||||
#define WITH_ONLY_FREE_BLOCKS 2
|
||||
|
||||
struct memory_cache
|
||||
{
|
||||
struct memory_cache *less;
|
||||
struct memory_cache *more;
|
||||
struct memory_cache *prev;
|
||||
struct memory_cache *next;
|
||||
struct __memory_slab *slab_list[3];
|
||||
unsigned int page_size;
|
||||
unsigned int free_slabs_left;
|
||||
unsigned short size;
|
||||
unsigned short original_size;
|
||||
int page_order;
|
||||
unsigned int blocks_per_slab;
|
||||
struct __memory_slab cache_slab; /* only used for __memory_cache_cache ! */
|
||||
};
|
||||
|
||||
static inline struct __memory_slab *__memory_push_slab (struct __memory_slab *head,struct __memory_slab *node)
|
||||
{
|
||||
node->less = head;
|
||||
if (head)
|
||||
{
|
||||
node->more = head->more;
|
||||
head->more = node;
|
||||
}
|
||||
else
|
||||
node->more = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline struct __memory_slab *__memory_pop_slab (struct __memory_slab *head,struct __memory_slab *node)
|
||||
{
|
||||
if (head)
|
||||
head->more = node->more;
|
||||
return node->more;
|
||||
}
|
||||
|
||||
static inline struct __memory_slab *__memory_move_slab (struct memory_cache *cache,int from,int to)
|
||||
{
|
||||
struct __memory_slab *head = cache->slab_list[from];
|
||||
cache->slab_list[from] = head->more;
|
||||
if (head->more)
|
||||
head->more->less = 0;
|
||||
head->more = cache->slab_list[to];
|
||||
if (head->more)
|
||||
head->more->prev = head;
|
||||
cache->slab_list[to] = head;
|
||||
return head;
|
||||
}
|
||||
|
||||
static struct memory_cache *__memory_cache_tree;
|
||||
static struct memory_cache *__memory_cache_cache;
|
||||
|
||||
static inline int __memory_get_order (unsigned size)
|
||||
{
|
||||
int order = 0;
|
||||
size = (size + MEMORY_PAGE_MINIMAL_SIZE - 1) & -MEMORY_PAGE_MINIMAL_SIZE;
|
||||
while (size > MEMORY_PAGE_MINIMAL_SIZE)
|
||||
{
|
||||
++order; size <<= 1;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
static inline struct __memory_slab *__memory_get_slab (struct memory_cache *cache,void *address)
|
||||
{
|
||||
return (struct __memory_slab *)((((unsigned)address + cache->page_size) & -cache->page_size) - sizeof (struct __memory_slab));
|
||||
}
|
||||
|
||||
static struct memory_cache *__memory_splay_cache (struct memory_cache *root,unsigned int left)
|
||||
{
|
||||
struct memory_cache *down;
|
||||
struct memory_cache *less;
|
||||
struct memory_cache *more;
|
||||
struct memory_cache *head[2];
|
||||
((struct memory_cache *)head->less =
|
||||
((struct memory_cache *)head->more = 0;
|
||||
less =
|
||||
more = &head;
|
||||
while (1)
|
||||
{
|
||||
if (left < root->free_slabs_left)
|
||||
{
|
||||
if ((down = root->less))
|
||||
{
|
||||
if (left < down->free_slabs_left)
|
||||
{
|
||||
root->less = down->more;
|
||||
down->more = root;
|
||||
root = down;
|
||||
if (!root->less)
|
||||
break;
|
||||
}
|
||||
more->less = root;
|
||||
more = root;
|
||||
root = root->less;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (root->free_slabs_left < left)
|
||||
{
|
||||
if ((down = root->more))
|
||||
{
|
||||
if (root->free_slabs_left < left)
|
||||
{
|
||||
root->more = down->less;
|
||||
down->less = root;
|
||||
root = down;
|
||||
if (!root->more)
|
||||
break;
|
||||
}
|
||||
less->more = root;
|
||||
less = root;
|
||||
root = root->more;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
less->more = root->less;
|
||||
more->less = root->more;
|
||||
root->less = ((struct memory_cache *)head->more;
|
||||
root->more = ((struct memory_cache *)head->less;
|
||||
return root;
|
||||
}
|
||||
|
||||
static inline struct memory_cache *__memory_insert_cache (struct memory_cache *root,struct memory_cache *node)
|
||||
{
|
||||
node->less =
|
||||
node->more =
|
||||
node->prev = 0;
|
||||
node->next = 0;
|
||||
if (root)
|
||||
{
|
||||
if (node->free_slabs_left == ((root = __memory_splay_cache (root,node))->free_slabs_left))
|
||||
{
|
||||
node->less = root.less;
|
||||
node->more = root.more;
|
||||
node->next = root;
|
||||
root->prev = node;
|
||||
}
|
||||
else if (node < root)
|
||||
{
|
||||
node->less = root->less;
|
||||
node->more = root;
|
||||
root->less = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
node->less = root;
|
||||
node->more = root->more;
|
||||
node->more = 0;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline struct memory_cache *__memory_remove_cache (struct memory_cache *root,struct memory_cache *node)
|
||||
{
|
||||
if (root)
|
||||
{
|
||||
if (node->prev)
|
||||
{
|
||||
node->prev->next = node->next;
|
||||
if (node->next)
|
||||
node->next->prev = node->prev;
|
||||
return node->prev;
|
||||
}
|
||||
root = __memory_splay_cache (root,node);
|
||||
if (root->less)
|
||||
{
|
||||
node = __memory_splay_page (root->less,node);
|
||||
node->more = root->more;
|
||||
}
|
||||
else
|
||||
node = root->more;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline struct memory_cache *__memory_move_cache (struct memory_cache *root,struct memory_cache *node,int delta)
|
||||
{
|
||||
if ((root = __memory_remove_cache (root,node)))
|
||||
{
|
||||
node->free_slabs_left += delta;
|
||||
root = __memory_insert_cache (root,node);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
static struct __memory_slab *__memory_grow_cache (struct memory_cache *cache)
|
||||
{
|
||||
struct __memory_slab *slab;
|
||||
unsigned int page;
|
||||
if (cache)
|
||||
{
|
||||
page = (unsigned int)memory_allocate_page (cache->page_order);
|
||||
if (page)
|
||||
{
|
||||
struct __memory_free_block *block,**link;
|
||||
slab = (struct __memory_slab *)(page + cache->page_size - sizeof (struct __memory_slab));
|
||||
slab->free_blocks_left = 0;
|
||||
link = &slab->free_block_list;
|
||||
for ((unsigned int)block = page;
|
||||
(unsigned int)block + cache->size < (unsigned int)slab;
|
||||
(unsigned int)block += cache->size)
|
||||
{
|
||||
*link = block;
|
||||
link = &block->link;
|
||||
++slab->free_blocks_left;
|
||||
}
|
||||
*link = 0;
|
||||
cache->blocks_per_slab = slab->free_blocks_left;
|
||||
cache->slab_list[WITH_ONLY_FREE_BLOCKS] =
|
||||
__memory_push_slab (cache->slab_list[WITH_ONLY_FREE_BLOCKS],slab);
|
||||
__memory_cache_tree = __memory_move_cache (__memory_cache_tree,cache,+1);
|
||||
return slab;
|
||||
}
|
||||
}
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
static int __memory_shrink_cache (struct memory_cache *cache,int all,int move)
|
||||
{
|
||||
struct __memory_slab *slab;
|
||||
unsigned int slabs = 0;
|
||||
if (cache)
|
||||
{
|
||||
while ((slab = cache->slab_list[WITH_ONLY_FREE_BLOCKS]))
|
||||
{
|
||||
++slabs;
|
||||
cache->slab_list[WITH_ONLY_FREE_BLOCKS] =
|
||||
__memory_pop_slab (cache->slab_list[WITH_ONLY_FREE_BLOCKS],slab);
|
||||
memory_release_page ((void *)((unsigned int)slab & -cache->page_size));
|
||||
if (all)
|
||||
continue;
|
||||
if (move)
|
||||
__memory_cache_tree = __memory_move_cache (__memory_cache_tree,cache,-slabs);
|
||||
return MEMORY_RETURN_SUCCESS;
|
||||
}
|
||||
}
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
struct memory_cache *memory_cache_create (unsigned int size,int align)
|
||||
{
|
||||
struct memory_cache *cache;
|
||||
unsigned int waste = 0,blocks_per_page;
|
||||
int page_order;
|
||||
unsigned int page_size;
|
||||
unsigned int original_size = size;
|
||||
|
||||
size = (align > 4) ? ((size + align - 1) & -align) : ((size + sizeof (int) - 1) & -sizeof (int));
|
||||
|
||||
if ((size >= MEMORY_MAX_PAGE_SIZE_PER_SLAB) ||
|
||||
(!(cache = memory_cache_allocate (__memory_cache_cache)))
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
|
||||
cache->free_slabs_left = 0;
|
||||
|
||||
cache->slab_list[ WITH_NO_FREE_BLOCK ] =
|
||||
cache->slab_list[WITH_SOME_FREE_BLOCKS] =
|
||||
cache->slab_list[WITH_ONLY_FREE_BLOCKS] = 0;
|
||||
|
||||
cache->original_size = original_size;
|
||||
cache->size = size;
|
||||
|
||||
page_size = 0;
|
||||
page_order = MEMORY_PAGE_MINIMAL_SIZE;
|
||||
|
||||
for (;; ++order,(page_size <<= 1))
|
||||
{
|
||||
if (page_order >= MEMORY_MAX_PAGE_ORDER_PER_SLAB)
|
||||
break;
|
||||
|
||||
waste = page_size;
|
||||
waste -= sizeof (struct __memory_slab);
|
||||
|
||||
blocks_per_slab = waste / size;
|
||||
waste -= block_per_slab * size;
|
||||
|
||||
if (blocks_per_slab < MEMORY_MIN_BLOCKS_PER_SLAB)
|
||||
{
|
||||
++page_order; page_size <<= 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* below 5% of lost space is correct */
|
||||
if ((waste << 16) / page_size) < 3276)
|
||||
break;
|
||||
++page_order; page_size <<= 1;
|
||||
}
|
||||
|
||||
cache->page_size = page_size;
|
||||
cache->page_order = page_order;
|
||||
|
||||
cache_tree = __memory_insert_cache (cache_tree,cache);
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
int memory_cache_destroy (struct memory_cache *cache)
|
||||
{
|
||||
/* this function shouldn't be called if there are still used blocks */
|
||||
if (cache && !cache->slab_list[WITH_NO_FREE_BLOCK] && !cache->slab_list[WITH_SOME_FREE_BLOCKS])
|
||||
{
|
||||
__memory_cache_tree = __memory_remove_cache (__memory_cache_tree,cache);
|
||||
if (__memory_shrink_cache (cache,1 /* release all free slabs */,0 /* don't move in cache_tree */))
|
||||
return memory_cache_release (__memory_cache_cache,cache);
|
||||
}
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
void *memory_cache_allocate (struct memory_cache *cache)
|
||||
{
|
||||
if (cache)
|
||||
{
|
||||
do
|
||||
{
|
||||
struct __memory_slab *slab;
|
||||
if ((slab = cache->slab_list[WITH_SOME_FREE_BLOCKS]))
|
||||
{
|
||||
if (slab->free_blocks_left > 0)
|
||||
{
|
||||
ok: struct __memory_free_block *block = slab->free_block_list;
|
||||
slab->free_block_list = block->link;
|
||||
if (--slab->free_blocks_left == 0)
|
||||
__memory_move_slab (WITH_SOME_FREE_BLOCKS,WITH_NO_FREE_BLOCK);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
if (cache->slab_list[WITH_FULL_FREE_BLOCKS])
|
||||
{
|
||||
slab = __memory_move_slab (WITH_ONLY_FREE_BLOCKS,WITH_SOME_FREE_BLOCKS);
|
||||
__memory_cache_tree = __memory_move_cache (__memory_cache_tree,cache,-1);
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
while (__memory_grow_cache (cache));
|
||||
}
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
int memory_cache_release (struct memory_cache *cache,void *address)
|
||||
{
|
||||
struct __memory_slab *slab = __memory_get_slab (cache,address);
|
||||
((struct __memory_free_block *)address)->link = slab->free_block_list;
|
||||
slab->free_block_list = (struct __memory_free_block *)address;
|
||||
if (slab->free_blocks_left++ == 0)
|
||||
__memory_move_slab (WITH_NO_FREE_BLOCK,WITH_SOME_FREE_BLOCKS);
|
||||
else if (slab->free_blocks_left == cache->blocks_per_slab)
|
||||
{
|
||||
__memory_move_slab (WITH_SOME_FREE_BLOCKS,WITH_ONLY_FREE_BLOCKS);
|
||||
__memory_cache_tree = __memory_move_cache (__memory_cache_tree,cache,+1);
|
||||
}
|
||||
return MEMORY_RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
static inline void __memory_cache_setup (void)
|
||||
{
|
||||
struct memory_cache *cache;
|
||||
struct __memory_slab *slab;
|
||||
struct __memory_free_block *block,**link;
|
||||
|
||||
cache = (struct memory_cache *)__memory_free_page;
|
||||
cache->original_size = sizeof (*cache);
|
||||
cache->size = sizeof (*cache);
|
||||
cache->page_size = MEMORY_PAGE_MINIMAL_SIZE;
|
||||
cache->page_order = MEMORY_PAGE_MINIMAL_ORDER;
|
||||
cache->free_slabs_left = 0;
|
||||
|
||||
slab = __memory_get_slab (cache,(void *)cache);
|
||||
|
||||
cache->slab_list[ WITH_NO_FREE_BLOCK ] =
|
||||
cache->slab_list[WITH_SOME_FREE_BLOCKS] =
|
||||
cache->slab_list[WITH_ONLY_FREE_BLOCKS] = 0;
|
||||
|
||||
link = &slab->free_block_list;
|
||||
for ((unsigned int)block = (unsigned int)cache;
|
||||
(unsigned int)block + sizeof (*cache) < (unsigned int)slab;
|
||||
(unsigned int)block += sizeof (*cache))
|
||||
{
|
||||
*link = block;
|
||||
link = &block->link;
|
||||
++slab->free_blocks_left;
|
||||
}
|
||||
*link = 0;
|
||||
cache->blocks_per_slab = slab->free_blocks_left + 1;
|
||||
cache->slab_list[WITH_SOME_FREE_BLOCKS] =
|
||||
__memory_push_slab (cache->slab_list[WITH_SOME_FREE_BLOCKS],slab);
|
||||
|
||||
__memory_cache_tree = __memory_insert_cache (__memory_cache_tree,cache);
|
||||
}
|
||||
|
||||
/*
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// MEMORY BLOCK //
|
||||
//////////////////
|
||||
//
|
||||
//
|
||||
*/
|
||||
|
||||
static struct memory_cache *__memory_free_block_cache[MEMORY_PAGE_MINIMAL_ORDER - 2];
|
||||
|
||||
static inline void *__memory_allocate_block (int order)
|
||||
{
|
||||
struct memory_cache *cache = __memory_free_block_cache[order - 2];
|
||||
do
|
||||
{
|
||||
if (cache)
|
||||
return memory_cache_allocate (cache);
|
||||
}
|
||||
while ((__memory_free_block_cache[order] = cache = memory_create_cache (size,0,0)));
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
static inline int __memory_release_block (int order,void *address)
|
||||
{
|
||||
struct memory_cache *cache = __memory_free_block_cache[order - 2];
|
||||
if (cache)
|
||||
return memory_cache_release (cache,address);
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
void *memory_block_allocate (int order)
|
||||
{
|
||||
if (order < 2) /* minimal size is 4 bytes */
|
||||
order = 2;
|
||||
if (order < MEMORY_PAGE_MINIMAL_ORDER)
|
||||
return __memory_allocate_block (order);
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
int memory_block_release (int order,void *address)
|
||||
{
|
||||
if (order < 2) /* minimal size is 4 bytes */
|
||||
order = 2;
|
||||
if (order < MEMORY_PAGE_MINIMAL_ORDER)
|
||||
return __memory_release_block (order,address);
|
||||
return MEMORY_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// MEMORY //
|
||||
////////////
|
||||
//
|
||||
//
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* NOT VERY OPTIMIZED AT ALL BUT WE WILL DO IT WHEN PRIORITY COMES */
|
||||
void memory_copy (void *target,void const *source,unsigned int count)
|
||||
{
|
||||
while (count--)
|
||||
*((char *)target)++ = *((char const *)source)++;
|
||||
}
|
||||
|
||||
/* NOT VERY OPTIMIZED AT ALL BUT WE WILL DO IT WHEN PRIORITY COMES */
|
||||
void memory_set (void *target,int byte,unsigned int count)
|
||||
{
|
||||
while (count--)
|
||||
*((char *)target)++ = (char)byte;
|
||||
}
|
||||
#endif
|
||||
|
||||
void memory_setup (void)
|
||||
{
|
||||
__memory_page_setup ();
|
||||
#if 0
|
||||
__memory_cache_setup ();
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// __________ __ ___. //
|
||||
// Open \______ \ ____ ____ | | _\_ |__ _______ ___ //
|
||||
// Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / //
|
||||
// Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < //
|
||||
// Software |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ //
|
||||
// \/ \/ \/ \/ \/ //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
/////////////////////////////////////
|
||||
// Copyright (C) 2002 by Alan Korr //
|
||||
/////////////////////////////////////
|
||||
//
|
||||
// All files in this archive are subject to the GNU General Public License.
|
||||
// See the file COPYING in the source tree root for full license agreement.
|
||||
//
|
||||
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
|
||||
// either express or implied.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#ifndef __MEMORY_H__
|
||||
#define __MEMORY_H__
|
||||
|
||||
enum
|
||||
{
|
||||
MEMORY_RETURN_SUCCESS = 1,
|
||||
MEMORY_RETURN_FAILURE = 0
|
||||
};
|
||||
|
||||
/*
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// MEMORY PAGE //
|
||||
/////////////////
|
||||
//
|
||||
//
|
||||
*/
|
||||
|
||||
#define MEMORY_PAGE_MINIMAL_ORDER ( 9) /* 512 bytes by default */
|
||||
#define MEMORY_PAGE_MAXIMAL_ORDER (21) /* 2 Mbytes by default */
|
||||
#define MEMORY_PAGE_MINIMAL_SIZE (1 << MEMORY_PAGE_MINIMAL_ORDER)
|
||||
#define MEMORY_PAGE_MAXIMAL_SIZE (1 << MEMORY_PAGE_MAXIMAL_ORDER)
|
||||
|
||||
#define MEMORY_TOTAL_PAGES (MEMORY_PAGE_MAXIMAL_SIZE / MEMORY_PAGE_MINIMAL_SIZE)
|
||||
#define MEMORY_TOTAL_ORDERS (1 + MEMORY_PAGE_MAXIMAL_ORDER - MEMORY_PAGE_MINIMAL_ORDER)
|
||||
|
||||
extern void *memory_page_allocate (int order);
|
||||
extern int memory_page_release (void *address);
|
||||
extern void memory_page_release_range (unsigned int start,unsigned int end);
|
||||
|
||||
/*
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// MEMORY CACHE //
|
||||
//////////////////
|
||||
//
|
||||
//
|
||||
*/
|
||||
|
||||
struct memory_cache;
|
||||
|
||||
extern struct memory_cache *memory_cache_create (unsigned int size,int align);
|
||||
extern int memory_cache_destroy (struct memory_cache *cache);
|
||||
extern void *memory_cache_allocate (struct memory_cache *cache);
|
||||
extern int memory_cache_release (struct memory_cache *cache,void *address);
|
||||
|
||||
/*
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// MEMORY BLOCK //
|
||||
//////////////////
|
||||
//
|
||||
//
|
||||
*/
|
||||
|
||||
extern void *memory_block_allocate (int order);
|
||||
extern int memory_block_release (int order,void *address);
|
||||
|
||||
/*
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// MEMORY //
|
||||
////////////
|
||||
//
|
||||
//
|
||||
*/
|
||||
|
||||
#define MEMORY_TOTAL_BYTES (MEMORY_PAGE_MAXIMAL_SIZE)
|
||||
|
||||
extern void memory_copy (void *target,void const *source,unsigned int count);
|
||||
extern void memory_set (void *target,int byte,unsigned int count);
|
||||
|
||||
extern void memory_setup (void);
|
||||
|
||||
/*
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
|
||||
OBJS1 = mytest.o
|
||||
TARGET1 = mytest
|
||||
|
||||
OBJS2 = Malloc.o
|
||||
TARGET2 = mtest
|
||||
|
||||
OBJS3 = dmytest.o
|
||||
TARGET3 = dmytest
|
||||
|
||||
# define this to talk a lot in runtime
|
||||
# -DDEBUG_VERBOSE
|
||||
CFLAGS = -g -Wall -DDEBUG -I../../malloc
|
||||
CC = gcc
|
||||
AR = ar
|
||||
|
||||
LDFLAGS = -L../../malloc -ldmalloc
|
||||
|
||||
all: $(TARGET1) $(TARGET2) $(TARGET3)
|
||||
|
||||
clean:
|
||||
rm -f core *~ $(TARGET1) $(TARGET2) $(TARGET3) \
|
||||
$(OBJS1) $(OBJS2) $(OBJS3)
|
||||
|
||||
$(TARGET1): $(OBJS1)
|
||||
$(CC) -g -o $(TARGET1) $(OBJS1) $(LDFLAGS)
|
||||
|
||||
$(TARGET2): $(OBJS2)
|
||||
$(CC) -g -o $(TARGET2) $(OBJS2) $(LDFLAGS)
|
||||
|
||||
$(TARGET3): $(OBJS3)
|
||||
$(CC) -g -o $(TARGET3) $(OBJS3) $(LDFLAGS)
|
||||
|
||||
dmytest.o: dmytest.c
|
||||
Malloc.o: Malloc.c
|
||||
mytest.o: mytest.c
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Storleken på allokeringen bestäms genom att först slumpas en position i
|
||||
"size_table" ut, sedan slumpas en storlek mellan den postionen och nästa värde
|
||||
i tabellen. Genom att ha tabellen koncentrerad med låga värden, så skapas
|
||||
flest såna. Rutinen håller på tills minnet en allokeringen nekas. Den kommer
|
||||
aldrig att ha mer än MAXIMAL_MEMORY_TO_ALLOCATE allokerat samtidigt. Maximalt
|
||||
har den MAX_ALLOCATIONS allokeringar samtidigt.
|
||||
|
||||
Statistiskt sätt så kommer efter ett tag MAX_ALLOCATIONS/2 allokeringar finnas
|
||||
samtidigt, med varje allokering i median med värdet av halva "size_table".
|
||||
|
||||
När minnet är slut (malloc()=NULL), frågas användaren om han ska fortsätta.
|
||||
|
||||
Med jämna mellanrum skrivs statisktik ut på skärmen. (DISPLAY_WHEN)
|
||||
|
||||
För att stressa systemet med fler små allokeringar, så kan man öka
|
||||
MAX_ALLOCATIONS. AMOUNT_OF_MEMORY bör få den att slå i taket fortare om man
|
||||
minskar det.
|
||||
|
||||
Ingen initiering görs av slumptalen, så allt är upprepbart (men plocka bort
|
||||
kommentaren på srand() och det löser sig.
|
||||
|
||||
*/
|
||||
|
||||
#define BMALLOC /* go go go */
|
||||
|
||||
#ifdef BMALLOC
|
||||
#include "dmalloc.h"
|
||||
|
||||
#include "bmalloc.h"
|
||||
#endif
|
||||
|
||||
#define MAX_ALLOCATIONS 100000
|
||||
#define AMOUNT_OF_MEMORY 100000 /* bytes */
|
||||
#define MAXIMAL_MEMORY_TO_ALLOCATE 49000 /* Sätt den här högre än
|
||||
AMOUNT_OF_MEMORY, och malloc() bör
|
||||
returnera NULL förr eller senare */
|
||||
|
||||
#define DISPLAY_WHEN (10000) /* When to display statistic */
|
||||
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define BOOL char
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
typedef struct {
|
||||
char *memory;
|
||||
long size;
|
||||
char filled_with;
|
||||
long table_position;
|
||||
} MallocStruct;
|
||||
|
||||
/*
|
||||
Skapar en lista med MAX_ALLOCATIONS storlek där det slumpvis allokeras
|
||||
eller reallokeras i.
|
||||
*/
|
||||
|
||||
MallocStruct my_mallocs[MAX_ALLOCATIONS];
|
||||
|
||||
long size_table[]={5,8,10,11,12,14,16,18,20,26,33,50,70,90,120,150,200,400,800,1000,2000,4000,8000};
|
||||
#define TABLESIZE ((sizeof(size_table)-1)/sizeof(long))
|
||||
long size_allocs[TABLESIZE];
|
||||
|
||||
int main(void)
|
||||
{
|
||||
long count=-1;
|
||||
long count_free=0, count_malloc=0, count_realloc=0;
|
||||
long total_memory=0;
|
||||
long out_of_memory=FALSE;
|
||||
|
||||
dmalloc_initialize();
|
||||
|
||||
#ifdef BMALLOC
|
||||
void *thisisourheap;
|
||||
thisisourheap = (malloc)(AMOUNT_OF_MEMORY);
|
||||
if(!thisisourheap)
|
||||
return -1; /* can't get memory */
|
||||
bmalloc_add_pool(thisisourheap, AMOUNT_OF_MEMORY);
|
||||
#endif
|
||||
|
||||
srand( 0 ); /* Initialize to a fixed random */
|
||||
|
||||
while (!out_of_memory) {
|
||||
long number=rand()%MAX_ALLOCATIONS;
|
||||
long size;
|
||||
long table_position=rand()%TABLESIZE;
|
||||
char fill_with=rand()&255;
|
||||
|
||||
count++;
|
||||
|
||||
size=rand()%(size_table[table_position+1]-
|
||||
size_table[table_position])+
|
||||
size_table[table_position];
|
||||
|
||||
/* fprintf(stderr, "number %d size %d\n", number, size); */
|
||||
|
||||
if (my_mallocs[number].size) { /* Om allokering redan finns på den här
|
||||
positionen, så reallokerar vi eller
|
||||
friar. */
|
||||
long old_size=my_mallocs[number].size;
|
||||
if (my_mallocs[number].size && fill_with<40) {
|
||||
free(my_mallocs[number].memory);
|
||||
total_memory -= my_mallocs[number].size;
|
||||
count_free++;
|
||||
size_allocs[my_mallocs[number].table_position]--;
|
||||
size=0;
|
||||
} else {
|
||||
/*
|
||||
* realloc() part
|
||||
*
|
||||
*/
|
||||
char *temp;
|
||||
#if 0
|
||||
if(my_mallocs[number].size > size) {
|
||||
printf("*** %d is realloc()ed to %d\n",
|
||||
my_mallocs[number].size, size);
|
||||
}
|
||||
#endif
|
||||
if (total_memory-old_size+size>MAXIMAL_MEMORY_TO_ALLOCATE)
|
||||
goto output; /* for-loop */
|
||||
temp = (char *)realloc(my_mallocs[number].memory, size);
|
||||
if (!temp)
|
||||
out_of_memory=size;
|
||||
else {
|
||||
my_mallocs[number].memory = temp;
|
||||
|
||||
my_mallocs[number].size=size;
|
||||
size_allocs[my_mallocs[number].table_position]--;
|
||||
size_allocs[table_position]++;
|
||||
total_memory -= old_size;
|
||||
total_memory += size;
|
||||
old_size=min(old_size, size);
|
||||
while (--old_size>0) {
|
||||
if (my_mallocs[number].memory[old_size]!=my_mallocs[number].filled_with)
|
||||
fprintf(stderr, "Wrong filling!\n");
|
||||
}
|
||||
count_realloc++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (total_memory+size>MAXIMAL_MEMORY_TO_ALLOCATE) {
|
||||
goto output; /* for-loop */
|
||||
}
|
||||
my_mallocs[number].memory=(char *)malloc(size); /* Allokera! */
|
||||
if (!my_mallocs[number].memory)
|
||||
out_of_memory=size;
|
||||
else {
|
||||
size_allocs[table_position]++;
|
||||
count_malloc++;
|
||||
total_memory += size;
|
||||
}
|
||||
}
|
||||
if(!out_of_memory) {
|
||||
my_mallocs[number].table_position=table_position;
|
||||
my_mallocs[number].size=size;
|
||||
my_mallocs[number].filled_with=fill_with;
|
||||
memset(my_mallocs[number].memory, fill_with, size);
|
||||
}
|
||||
output:
|
||||
if (out_of_memory || !(count%DISPLAY_WHEN)) {
|
||||
printf("(%ld) malloc %ld, realloc %ld, free %ld, total size %ld\n",
|
||||
count, count_malloc, count_realloc, count_free, total_memory);
|
||||
{
|
||||
int count;
|
||||
printf("[size bytes]=[number of allocations]\n");
|
||||
for (count=0; count<TABLESIZE; count++) {
|
||||
printf(" %ld=%ld\n", size_table[count], size_allocs[count]);
|
||||
}
|
||||
printf("\n\n");
|
||||
}
|
||||
}
|
||||
if (out_of_memory) {
|
||||
if(out_of_memory)
|
||||
printf("Couldn't get %ld bytes\n", out_of_memory);
|
||||
|
||||
dmalloc_status();
|
||||
bmalloc_status();
|
||||
|
||||
fprintf(stderr, "Memory is out! Continue (y/n)");
|
||||
switch (getchar()) {
|
||||
case 'y':
|
||||
case 'Y':
|
||||
out_of_memory=FALSE;
|
||||
break;
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dmalloc.h"
|
||||
#include "bmalloc.h"
|
||||
|
||||
#define MAX 500
|
||||
#define MAX2 1000
|
||||
#define MAXC 2
|
||||
|
||||
#define TESTA
|
||||
#define TESTB
|
||||
#define TESTC
|
||||
#define TESTD
|
||||
|
||||
int test1(void)
|
||||
{
|
||||
#define MAXK 100
|
||||
int i;
|
||||
void *wow[MAXK];
|
||||
for(i=0; i<MAXK; i++)
|
||||
if(!(wow[i]=malloc(412))) {
|
||||
printf("*** Couldn't allocate memory, exiting\n");
|
||||
return -2;
|
||||
}
|
||||
for(i=MAXK-1; i>=0; i-=2)
|
||||
free(wow[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test2(void)
|
||||
{
|
||||
#define MAXS 10
|
||||
#define MAXS1 0
|
||||
int i;
|
||||
void *ptr[MAXS];
|
||||
|
||||
for(i=MAXS1; i< MAXS; i++) {
|
||||
printf("%d malloc(%d)\n", i, i*55);
|
||||
ptr[i] = malloc (i*55);
|
||||
}
|
||||
for(i=MAXS1; i< MAXS; i++) {
|
||||
void *tmp;
|
||||
printf("%d realloc(%d)\n", i, i*155);
|
||||
tmp=realloc(ptr[i], i*155);
|
||||
if(tmp)
|
||||
ptr[i] = tmp;
|
||||
}
|
||||
for(i=MAXS1; i< MAXS; i++) {
|
||||
if(ptr[i]) {
|
||||
printf("%d free(%d)\n", i, i*155);
|
||||
free(ptr[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test3(void)
|
||||
{
|
||||
int i;
|
||||
void *ptr[MAXC];
|
||||
printf("This is test C:\n");
|
||||
|
||||
for(i=0; i< MAXC; i++) {
|
||||
printf("%d malloc(100)\n", i+1);
|
||||
ptr[i] = malloc(100);
|
||||
printf(" ...returned %p\n", ptr[i]);
|
||||
}
|
||||
|
||||
for(i=0; i< MAXC; i++) {
|
||||
printf("%d free()\n", i+1);
|
||||
if(ptr[i])
|
||||
free(ptr[i]);
|
||||
}
|
||||
|
||||
printf("End of test C:\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test4(void)
|
||||
{
|
||||
int i;
|
||||
int memory = 0;
|
||||
void *pointers[MAX];
|
||||
printf("This is test I:\n");
|
||||
|
||||
for(i=0; i<MAX; i++) {
|
||||
printf("%d attempts malloc(%d)\n", i, i*6);
|
||||
pointers[i]=malloc(i*6);
|
||||
if(!pointers[i]) {
|
||||
printf("cant get more memory!");
|
||||
return(0);
|
||||
}
|
||||
memory += (i*6);
|
||||
}
|
||||
printf("\namount: %d\n", memory);
|
||||
memory = 0;
|
||||
for(i=0; i<MAX; i++) {
|
||||
printf("%d attempts realloc(%d)\n", i, i*7);
|
||||
pointers[i]=realloc(pointers[i], i*7);
|
||||
memory += i*7;
|
||||
}
|
||||
printf("\namount: %d\n", memory);
|
||||
for(i=0; i<MAX; i++) {
|
||||
printf("%d attempts free(%d)\n", i, i*7);
|
||||
free(pointers[i]);
|
||||
}
|
||||
printf("\nend of test 1\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test5(void)
|
||||
{
|
||||
int memory = 0;
|
||||
int i;
|
||||
void *pointers2[MAX2];
|
||||
memory = 0;
|
||||
printf("\nTest II\n");
|
||||
for(i=0; i< MAX2; i++) {
|
||||
printf("%d attempts malloc(%d)\n", i, 7);
|
||||
pointers2[i] = malloc(7);
|
||||
memory += 7;
|
||||
}
|
||||
printf("\namount: %d\n", memory);
|
||||
for(i=0; i< MAX2; i++) {
|
||||
if(pointers2[i])
|
||||
free(pointers2[i]);
|
||||
}
|
||||
printf("\nend of test II\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HEAPSIZE 10000
|
||||
|
||||
void smallblocks(void)
|
||||
{
|
||||
void *ptr;
|
||||
int i=0;
|
||||
do {
|
||||
|
||||
ptr = malloc(16);
|
||||
i++;
|
||||
} while(ptr);
|
||||
|
||||
printf("I: %d\n", i);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *heap = (malloc)(HEAPSIZE);
|
||||
if(!heap)
|
||||
return -1;
|
||||
dmalloc_initialize();
|
||||
bmalloc_add_pool(heap, HEAPSIZE);
|
||||
|
||||
smallblocks();
|
||||
|
||||
bmalloc_status();
|
||||
dmalloc_status();
|
||||
|
||||
return 0;
|
||||
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "bmalloc.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *pointers[5];
|
||||
int i;
|
||||
void *area;
|
||||
|
||||
for(i=0; i<5; i++)
|
||||
pointers[i] = malloc(8000);
|
||||
|
||||
if(argc>1) {
|
||||
switch(argv[1][0]) {
|
||||
case '1':
|
||||
for(i=0; i<5; i++) {
|
||||
bmalloc_add_pool(pointers[i], 4000);
|
||||
bmalloc_add_pool((char *)pointers[i]+4000, 4000);
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
area = malloc(20000);
|
||||
bmalloc_add_pool(area, 3000);
|
||||
bmalloc_add_pool((char *)area+6000, 3000);
|
||||
bmalloc_add_pool((char *)area+3000, 3000);
|
||||
bmalloc_add_pool((char *)area+12000, 3000);
|
||||
bmalloc_add_pool((char *)area+9000, 3000);
|
||||
break;
|
||||
case '3':
|
||||
{
|
||||
void *ptr[10];
|
||||
area = malloc(20000);
|
||||
bmalloc_add_pool(area, 20000);
|
||||
|
||||
printf(" ** TEST USAGE\n");
|
||||
for(i=0; i<9; i++)
|
||||
ptr[i]=bmalloc(200);
|
||||
bmalloc_status();
|
||||
for(i=0; i<9; i++)
|
||||
bfree(ptr[i]);
|
||||
printf(" ** END OF TEST USAGE\n");
|
||||
}
|
||||
|
||||
break;
|
||||
case '4':
|
||||
{
|
||||
void *ptr[10];
|
||||
area = malloc(20000);
|
||||
bmalloc_add_pool(area, 20000);
|
||||
|
||||
ptr[0]=bmalloc(4080);
|
||||
bmalloc_status();
|
||||
bfree(ptr[0]);
|
||||
printf(" ** END OF TEST USAGE\n");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
for(i=4; i>=0; i--)
|
||||
bmalloc_add_pool(pointers[i], 8000-i*100);
|
||||
|
||||
bmalloc_status();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
# __________ __ ___.
|
||||
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
# \/ \/ \/ \/ \/
|
||||
# $Id$
|
||||
#
|
||||
|
||||
FIRMWARE = ../..
|
||||
COMMON = $(FIRMWARE)/common
|
||||
DRIVERS = $(FIRMWARE)/drivers
|
||||
|
||||
CC = gcc
|
||||
LD = ld
|
||||
AR = ar
|
||||
AS = as
|
||||
OC = objcopy
|
||||
scramble = scramble-win32
|
||||
DEFINES = -DCRT_DISPLAY -DDEBUG -DSIMULATOR
|
||||
INCLUDES=-I. -I$(FIRMWARE) -I$(COMMON) -I$(DRIVERS)
|
||||
TARGET_OPTIONS =
|
||||
CFLAGS = -g -Wall ${TARGET_OPTIONS} -Wstrict-prototypes $(INCLUDES) $(DEFINES)
|
||||
|
||||
SRC := playlist.c settings.c panic.c disk.c debug.c harness.c
|
||||
OBJS := $(SRC:%.c=%.o)
|
||||
|
||||
%.o: %.s
|
||||
$(CC) -o $@ $(CFLAGS) $(INCLUDES) $(DEFS) -c $<
|
||||
|
||||
all : rockbox
|
||||
|
||||
rockbox: $(OBJS)
|
||||
$(CC) -o $@ ${OBJS}
|
||||
|
||||
playlist.o:$(FIRMWARE)/playlist.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
settings.o:$(FIRMWARE)/settings.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
panic.o:$(FIRMWARE)/panic.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
disk.o:$(FIRMWARE)/disk.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
debug.o:$(FIRMWARE)/debug.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
dist:
|
||||
tar czvf dist.tar.gz Makefile main.c start.s app.lds
|
||||
|
||||
clean:
|
||||
-rm -f *.x *.i *.o *.elf *.bin *.map *.mod *.bak *~
|
||||
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
# __________ __ ___.
|
||||
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
# \/ \/ \/ \/ \/
|
||||
# $Id$
|
||||
#
|
||||
|
||||
# Pick a target to build for
|
||||
TARGET = -DARCHOS_PLAYER=1
|
||||
#TARGET = -DARCHOS_PLAYER_OLD=1
|
||||
#TARGET = -DARCHOS_RECORDER=1
|
||||
|
||||
CC = gcc
|
||||
LD = ld
|
||||
AR = ar
|
||||
AS = as
|
||||
OC = objcopy
|
||||
scramble = scramble-win32
|
||||
DEFINES = -DCRT_DISPLAY
|
||||
INCLUDES=-I. -Icommon -Idrivers
|
||||
TARGET_OPTIONS =
|
||||
CFLAGS = -Os -Wall ${TARGET_OPTIONS} -nostdlib -Wstrict-prototypes -fomit-frame-pointer -fschedule-insns $(INCLUDES) $(DEFINES) $(TARGET)
|
||||
AFLAGS += -small -relax
|
||||
|
||||
SRC := playlist.c settings.c panic.c disk.c debug.c harness.c
|
||||
OBJS := $(SRC:%.c=%.o)
|
||||
|
||||
%.o: %.s
|
||||
$(CC) -o $@ $(CFLAGS) $(INCLUDES) $(DEFS) -c $<
|
||||
|
||||
all : rockbox-win32.exe
|
||||
|
||||
rockbox-win32.exe: $(OBJS)
|
||||
$(CC) -o rockbox-win32.exe ${OBJS}
|
||||
|
||||
dist:
|
||||
tar czvf dist.tar.gz Makefile main.c start.s app.lds
|
||||
|
||||
clean:
|
||||
-rm -f *.x *.i *.o *.elf *.bin *.map *.mod *.bak *~
|
||||
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
|
||||
Playlists on the Rockbox
|
||||
|
||||
1. Demand-loading of Playlist Filenames from Disk
|
||||
|
||||
A playlist is simply a list of track names. These lists can either be
|
||||
created dynamically by the user, or they can be predefined and placed
|
||||
into a text file with an .m3u extension.
|
||||
|
||||
The 2048 KB of RAM is the reason we need to get this right. If an average
|
||||
track name (i.e. \music\cure\disintegration\01-pictures_of_you.mp3)
|
||||
is assumed to be 50 characters long, then:
|
||||
|
||||
A playlist of 15 tracks is 15 * 50 ~= 750 bytes
|
||||
A playlist of 100 tracks is 100 * 50 ~= 5 kilobytes
|
||||
A playlist of 3500 tracks is 3500 * 50 ~= 175 kilobytes
|
||||
A playlist of 10000 tracks is 10000 * 50 ~= 1/4 megabyte
|
||||
|
||||
From these figures, it can be seen that for large playlists, storing
|
||||
the entire list of track names in memory significantly reduces the
|
||||
capacity available to the audio data buffer, which in turn has a negative
|
||||
impact on the performance of the system.
|
||||
|
||||
One method of reducing the total memory consumption of a playlist is
|
||||
to delay bringing the actual filenames into memory until needed. Instead,
|
||||
the playlist text file can be scanned, and an in-memory array constructed
|
||||
with one element for each track present in the text file. Progressing
|
||||
through the playlist entails getting the appropriate entry from the array,
|
||||
and using that to perform a lookup of the corresponding filename entry
|
||||
from the playlist text file.
|
||||
|
||||
With this method, and given that an integer on the Rockbox's CPU is 4 bytes:
|
||||
|
||||
A playlist of 15 tracks is 15 * 4 ~= 60 bytes
|
||||
A playlist of 100 tracks is 100 * 4 ~= 400 bytes
|
||||
A playlist of 3500 tracks is 3500 * 4 ~= 13 kilobytes
|
||||
A playlist of 10000 tracks is 10000 * 4 ~= 39 kilobytes
|
||||
|
||||
It is clear that these are substantial savings, albeit at the cost of
|
||||
increased complexity and disk i/o. Prefetch strategies could improve
|
||||
performance compared to demand-loading a single entry.
|
||||
|
||||
2. Implementation Options
|
||||
|
||||
Keeping the track names in a file on disk is easy enough for a predefined
|
||||
m3u playlist, but for dynamically created playlists, where the user
|
||||
browses the filesystem and adds tracks or entire directory hierarchies
|
||||
at will, we will need to store the playlist track names in a dedicated
|
||||
file. This will be called the Anonymous Playlist, the location of which
|
||||
can be set by the user, but will default to \anonymous.m3u or somesuch.
|
||||
The current contents of the Anonymous Playlist can be named and saved at
|
||||
any time.
|
||||
|
||||
The data structure to support playlists would therefore be:
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char filename[256] ; /* path name of m3u playlist on disk */
|
||||
int *indices; /* array of indices into the playlist */
|
||||
int index; /* index of current track within playlist */
|
||||
} playlist_info_t;
|
||||
|
||||
So far, so good: we read from an existing m3u file, or we create an
|
||||
anonymous one. But what do we do if we start with an existing m3u file,
|
||||
and then the user wants to dynamically add tracks to it? A few options
|
||||
exist:
|
||||
|
||||
a) we disallow playlist modification of existing m3u files, offering
|
||||
instead to replace the current playlist with the new one.
|
||||
|
||||
b) we give the user the option of appending the new tracks to the
|
||||
existing m3u file.
|
||||
|
||||
c) we copy the contents of the existing m3u playlist to the anonymous one,
|
||||
and then append the new tracks to that. If the m3u playlist is large,
|
||||
this could be wasteful and potentially time-consuming. However, choosing
|
||||
this option would provide the facility to insert or append entire
|
||||
existing m3u playlists 'into' one another, a feature missng from the
|
||||
commercial firmware.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by wavey@wavey.org
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <common/track.h>
|
||||
#include "settings.h"
|
||||
#include "playlist.h"
|
||||
#include "panic.h"
|
||||
#include "disk.h"
|
||||
#include "debug.h"
|
||||
#include "config.h"
|
||||
#include "harness.h"
|
||||
|
||||
/* global string for panic display */
|
||||
|
||||
char panic_message[128];
|
||||
|
||||
/*
|
||||
* entrypoint
|
||||
*/
|
||||
int main( int argc, char **args )
|
||||
{
|
||||
/* allocate runtime data structures */
|
||||
|
||||
user_settings_t settings;
|
||||
playlist_info_t playlist;
|
||||
|
||||
debugf( "\nrockbox test harness started.\n" );
|
||||
|
||||
/* populate runtime data structures */
|
||||
|
||||
initialise( &settings, &playlist );
|
||||
|
||||
/* begin tests */
|
||||
|
||||
start( &settings, &playlist );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* populate runtime data structures
|
||||
*/
|
||||
void initialise( user_settings_t *settings, playlist_info_t *playlist )
|
||||
{
|
||||
debugf( "init()\n" );
|
||||
|
||||
reload_all_settings( settings );
|
||||
reload_playlist_info( playlist );
|
||||
}
|
||||
|
||||
/*
|
||||
* start tests
|
||||
*/
|
||||
void start( user_settings_t *settings, playlist_info_t *playlist )
|
||||
{
|
||||
track_t track;
|
||||
|
||||
debugf( "start()\n" );
|
||||
|
||||
/* show current values */
|
||||
|
||||
display_current_settings( settings );
|
||||
display_current_playlist( playlist );
|
||||
|
||||
/* wipe playlist contents */
|
||||
|
||||
empty_playlist( playlist );
|
||||
display_current_playlist( playlist );
|
||||
|
||||
/* user selects a new playlist */
|
||||
|
||||
load_playlist( playlist, "test2.m3u" );
|
||||
display_current_playlist( playlist );
|
||||
|
||||
/* randomise playlist */
|
||||
|
||||
randomise_playlist( playlist, 45678 );
|
||||
display_current_playlist( playlist );
|
||||
|
||||
/* get next track in playlist */
|
||||
|
||||
track = next_playlist_track( playlist );
|
||||
display_playlist_track( &track );
|
||||
|
||||
/* get next track in playlist */
|
||||
|
||||
track = next_playlist_track( playlist );
|
||||
display_playlist_track( &track );
|
||||
}
|
||||
#ifdef SIMULATOR
|
||||
void app_main ()
|
||||
{
|
||||
main (0, NULL);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by wavey@wavey.org
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include "settings.h"
|
||||
#include "playlist.h"
|
||||
|
||||
void initialise( user_settings_t *settings, playlist_info_t *playlist );
|
||||
void start( user_settings_t *settings, playlist_info_t *playlist );
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
testing
|
||||
testing ssh keys
|
||||
2
|
||||
3
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#define _PAGE_ Rockbox on FM Recorder - Status
|
||||
#include "head.t"
|
||||
|
||||
Known bugs (April 7, 2003):
|
||||
<ul>
|
||||
<li> No radio support at all
|
||||
</ul>
|
||||
|
||||
#include "foot.t"
|
||||
Loading…
Add table
Add a link
Reference in a new issue