mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-05-03 03:32:44 -07:00
613 lines
20 KiB
Text
613 lines
20 KiB
Text
# Confidential: no. Readership: MPS users. Status: complete.
|
|
# Author: RHSK 2006-02-17. Copyright (C) 2006 Ravenbrook Limited
|
|
# <http://www.ravenbrook.com/>. All rights reserved.
|
|
# See end of file for license.
|
|
# $Id$
|
|
|
|
|
|
This is the "hello-world" example; it shows you how to call the MPS from
|
|
your code.
|
|
|
|
To begin, get a copy of the MPS-kit from:
|
|
<http://www.ravenbrook.com/project/mps/release/>
|
|
|
|
Unpack the MPS-kit. (You don't need to compile anything yet, so you can
|
|
skip the MPS-kit readme.txt file for now).
|
|
|
|
The MPS-kit unpacks to a directory named "mps-kit-1.108.2" (or similar),
|
|
with subdirectories like this:
|
|
|
|
mps-kit-1.108.2/
|
|
code/
|
|
design/
|
|
example/
|
|
manual/
|
|
procedure/
|
|
test/
|
|
tool/
|
|
|
|
The MPS is a software library, implemented in C. The types, functions,
|
|
macros, constants, etc (collectively called "facilities") are declared
|
|
in the MPS header files: your code must '#include' the MPS header files
|
|
for the facilities you use. The facilities are implemented in the MPS
|
|
libraries: your code must link with the libraries containing the
|
|
facilities you use. The examples below show how to do this.
|
|
|
|
|
|
The "mps.h" header file
|
|
-----------------------
|
|
|
|
Here is a tiny C program to #include the "mps.h" header file and check
|
|
that it compiles:
|
|
|
|
/* Demo File: 01checkmpsheader.c */
|
|
/* Simple C client of the MPS */
|
|
|
|
#include "mps.h"
|
|
|
|
int main(void)
|
|
{
|
|
mps_arena_t ArenaDemo;
|
|
return 0; /* success */
|
|
}
|
|
|
|
The "mps.h" header file declares the major (non-optional) MPS
|
|
facilities, and all MPS client code needs to #include it. Find the
|
|
"mps.h" header file in the "code" subdirectory of the MPS-kit:
|
|
|
|
mps-kit-1.108.2/code/mps.h
|
|
|
|
For now, just put a copy of "mps.h" into a working directory where you
|
|
can try the following examples. Now you can compile and run
|
|
"01checkmpsheader.c". On Unix you might use:
|
|
|
|
% gcc 01checkmpsheader.c
|
|
% ./a.out && echo "success"
|
|
|
|
|
|
Choosing an arena class
|
|
-----------------------
|
|
|
|
The first step in using the MPS is to create an "arena". Inside this
|
|
arena you will create "pools". Inside the pools you will create
|
|
"objects". Arenas, pools, and objects are abstract data types: you must
|
|
use MPS functions to create, change, and destroy them.
|
|
|
|
The arena abstracts the source of memory from which MPS will allocate:
|
|
there are different classes of arena for different sorts of memory
|
|
source. We will start with the "CL" arena class -- the "CL" stands for
|
|
"client". A CL arena takes a big block of memory provided by the client
|
|
(our demo program), and uses that as the source of memory in which to
|
|
create pools and objects.
|
|
|
|
The code to create a CL arena will look roughly like this:
|
|
mps_arena_create(&ArenaDemo, mps_arena_class_cl(), arg1, arg2, ...)
|
|
|
|
Notice that the "CL" arena class is an abstract constant: the constant
|
|
is not compiled into your code; instead, you call the MPS function
|
|
mps_arena_class_cl() to obtain it. (Most MPS constants work this way;
|
|
it avoids some versioning issues, and makes it easier to port the MPS to
|
|
tricky platforms).
|
|
|
|
|
|
Additional MPS header files
|
|
---------------------------
|
|
|
|
The "mps_arena_class_cl" function is declared in the additional MPS
|
|
header file "mpsacl.h" ("mpsa" stands for "MPS Arena class"). So put a
|
|
copy of "mpsacl.h" into your working directory. The source code to use
|
|
it looks like this:
|
|
|
|
/* Demo File: 02checkarenaclassheader.c */
|
|
/* Simple C client of the MPS */
|
|
|
|
#include "mps.h"
|
|
#include "mpsacl.h" /* for mps_arena_class_cl */
|
|
|
|
int main(void)
|
|
{
|
|
mps_arena_t ArenaDemo;
|
|
mps_arena_class_t ArenaClassDemo = NULL;
|
|
|
|
ArenaClassDemo = mps_arena_class_cl();
|
|
return 0; /* success */
|
|
}
|
|
|
|
To compile (but not link) this on Unix you might use the following
|
|
command. (The "-c" tells gcc to stop after compilation).
|
|
|
|
% gcc -c 02checkarenaclassheader.c
|
|
|
|
However, when you try to link, it will fail because we don't have the
|
|
MPS library to hand:
|
|
|
|
% gcc 02checkarenaclassheader.c
|
|
% # (this will fail)
|
|
|
|
|
|
The MPS library: "mps.a"
|
|
------------------------
|
|
|
|
The MPS library is available on many platforms: Windows, many Unixes,
|
|
Mac OS X, etc. See the MPS-kit readme.txt file for details. For
|
|
example, to compile the MPS library for Mac OS X, you might use this:
|
|
|
|
% cd mps-kit-1.108.2/code; make -f xcppgc.gmk VARIETY=ci mps.a
|
|
|
|
Briefly: "-f xcppgc.gmk" tells the buildsystem to use the makefile for
|
|
Mac OS X ("xc"), for the PowerPC architecture ("pp"), using the GCC
|
|
compiler ("gc"). "VARIETY=ci" tells it to build the 'cool internal'
|
|
variety, which includes asserts and other run-time checks.
|
|
|
|
Object files and libraries are placed in a separate subdirectory,
|
|
according to platform and variety: using the command above makes the
|
|
library in "xcppgc/ci/mps.a". (The "mps.a" on the "make" command-line
|
|
is a predefined target that the buildsystem recognises).
|
|
|
|
There is nothing magical about "mps.a": it is just a collection of
|
|
object files built from MPS C source files. You can tailor your own
|
|
collection with extras or omissions. The MPS-kit build system is
|
|
configured to build an "mps.a" with the 'most commonly used' files,
|
|
selecting the appropriate variant where there is a platform dependent
|
|
choice.
|
|
|
|
However, when we try to link with it, we find that the MPS library
|
|
itself has dependencies:
|
|
|
|
% gcc 02checkarenaclassheader.c mps-kit-1.108.2/code/xcppgc/ci/mps.a
|
|
% # (this will fail)
|
|
|
|
|
|
The MPS plinth: "mpsplan.a"
|
|
---------------------------
|
|
|
|
The dependency is because the MPS needs to call some functions in the
|
|
client code (our demo program). The MPS can do this in two different
|
|
ways: through the plinth interface, or through callbacks. The plinth
|
|
interface is for general purpose utility functions, such as
|
|
"mps_lib_memcpy" and "mps_lib_assert_fail". The client must provide
|
|
these utility functions, and the MPS uses them. The idea here is that
|
|
the MPS should not assume how the client wants these jobs to be done.
|
|
|
|
(Note: The MPS also makes some direct operating-system calls.
|
|
Platform-specific source code files in the MPS use direct calls to
|
|
cooperate with the operating-system in a platform-dependent manner, such
|
|
as to write-protect pages of memory. These calls do not involve client
|
|
code. In contrast, the plinth interface deals with platform-independent
|
|
concepts that the client may have an interest in.)
|
|
|
|
We don't have to write our own plinth: the MPS-kit includes an example
|
|
plinth that uses the standard C library. This example plinth is called
|
|
"mpsplan.a". ("pl" means plinth; "an" means "ANSI", ie. the standard C
|
|
library).
|
|
|
|
To build the example plinth for Mac OS X, use:
|
|
|
|
% cd mps-kit-1.108.2/code; make -f xcppgc.gmk VARIETY=ci mpsplan.a
|
|
|
|
Now we can link and run our simple demo file:
|
|
|
|
% gcc 02checkarenaclassheader.c mps-kit-1.108.2/code/xcppgc/ci/mps.a mps-kit-1.108.2/code/xcppgc/ci/mpsplan.a
|
|
% ./a.out && echo "success"
|
|
|
|
|
|
Recap
|
|
-----
|
|
|
|
We have now found and are using:
|
|
- the main "mps.h" header file;
|
|
- the additional MPS header file "mpsacl.h";
|
|
- (in "mpsacl.h") the identifier function "mps_arena_class_cl"
|
|
for the "CL" arena class we want to use;
|
|
|
|
We have built and are using:
|
|
- the MPS library "mps.a";
|
|
- the example plinth "mpsplan.a".
|
|
|
|
Now we can start to use more MPS functions. In the next three sections
|
|
we will create an arena (of class "CL"), create a pool (of class "MV"),
|
|
and then allocate some memory.
|
|
|
|
|
|
Creating an arena
|
|
-----------------
|
|
|
|
Look up the reference manual entry for mps_arena_class_cl() at
|
|
<http://www.ravenbrook.com/project/mps/master/manual/reference/#
|
|
mps_arena_class_cl>. The notes and example code in the manual entry
|
|
describe the additional parameters that we need to pass to
|
|
"mps_arena_create()". For a "CL" class arena, we first allocate a big
|
|
block of memory, and then call mps_arena_create passing the count of
|
|
bytes and a pointer to the first byte:
|
|
|
|
/* Demo File: 03arena_create.c */
|
|
/* Simple C client of the MPS */
|
|
|
|
#include <stdlib.h> /* for malloc */
|
|
#include <stdio.h> /* for printf */
|
|
|
|
#include "mps.h"
|
|
#include "mpsacl.h" /* for mps_arena_class_cl */
|
|
|
|
int main(void)
|
|
{
|
|
void *pBlock = NULL;
|
|
size_t cbBlock = 1024 * 1024;
|
|
mps_arena_t ArenaDemo = NULL;
|
|
|
|
mps_res_t res;
|
|
|
|
pBlock = malloc(cbBlock);
|
|
if(pBlock == NULL) {
|
|
printf("malloc failed!\n");
|
|
exit(1);
|
|
}
|
|
|
|
res = mps_arena_create(
|
|
&ArenaDemo,
|
|
mps_arena_class_cl(),
|
|
cbBlock,
|
|
pBlock
|
|
);
|
|
switch (res) {
|
|
case MPS_RES_OK:
|
|
break;
|
|
case MPS_RES_MEMORY:
|
|
printf("mps_arena_create: cbBlock too small\n");
|
|
exit(2);
|
|
case MPS_RES_FAIL:
|
|
printf(
|
|
"mps_arena_create: refused; "
|
|
"see MPS reference manual\n"
|
|
);
|
|
exit(2);
|
|
}
|
|
|
|
/* rest of program */
|
|
printf("Success.\n");
|
|
return 0;
|
|
}
|
|
|
|
To run this file:
|
|
|
|
% gcc 03arena_create.c mps-kit-1.108.2/code/xcppgc/ci/mps.a mps-kit-1.108.2/code/xcppgc/ci/mpsplan.a
|
|
% ./a.out
|
|
|
|
|
|
Creating a pool
|
|
---------------
|
|
|
|
Before allocating memory, we must create a "pool". A pool abstracts a
|
|
collection of objects that are managed in the same way. The "way an
|
|
object is managed" means the protocol used by the client and MPS to
|
|
create, maintain, and destroy the object.
|
|
|
|
We will create a pool of the "MV" pool class. "MV" roughly stands for
|
|
"manual, variable". "Manual" means the client explicitly tells the MPS
|
|
when to free the object (in contrast to an "automatic" protocol, ie.
|
|
with automatic garbage collection, where the client never calls free).
|
|
"Variable" means the pool can hold objects of varying sizes.
|
|
|
|
The functions we need are "mps_class_mv()" and "mps_pool_create()" --
|
|
these are analogous to the "mps_arena_class_cl" and "mps_arena_create"
|
|
we needed for creating an arena. [Note the inconsistent naming:
|
|
"mps_class_mv" should really be called "mps_pool_class_mv".]
|
|
|
|
Put a copy of "mpscmv.h" into your working directory ("mpsc" stands for
|
|
"MPS (pool) Class"; "MV" is the name of the pool class). It declares
|
|
the "mps_class_mv" function, and some useful pool-class-specific
|
|
functions. We will use "mps_class_mv()" and "mps_mv_free_size()".
|
|
|
|
If you attempt to look up the reference manual entries for
|
|
"mps_class_mv()" and "mps_pool_create()", you will find they are
|
|
missing. Section 4 of the reference manual lists them as valid
|
|
client-callable functions, but currently undocumented:
|
|
|
|
<http://www.ravenbrook.com/project/mps/master/manual/reference/#section-4>
|
|
|
|
Don't despair. Look in the "code" subdirectory of the MPS-kit, and find
|
|
the file "poolmv.h".
|
|
|
|
The "poolmv.h" file is internal to the MPS, and contains part of the
|
|
implementation of the "MV" pool class. The MPS tries hard to keep its
|
|
external interface (header files beginning "mps...") separate from its
|
|
internal details. Where the external documentation is lacking, you can
|
|
look at internal materials such as design documentation, implementation
|
|
files, and module test files, but be aware that you have crossed over to
|
|
the 'internal world' of the MPS. Two differences you may notice in
|
|
internal code are that the types used are different, and function names
|
|
are different. It should go without saying that your client code should
|
|
never rely on the particular details of the MPS implementation. If you
|
|
(as an MPS user writing client code) find yourself needing to look
|
|
inside the MPS implementation, that suggests there is a fault in the
|
|
external documentation: please contact us and let us know.
|
|
|
|
The parameters used to create a pool of class "MV" are discussed in the
|
|
comments at the top of the "poolmv.h" file. When creating a pool of
|
|
class "MV", mps_pool_create requires three extra arguments, which hold
|
|
tuning parameters that help the pool to perform its tasks efficiently.
|
|
The first and third arguments relate to the size of the pool; the second
|
|
argument relates to the size of objects in the pool. (See the comments
|
|
in "poolmv.h" for more detail).
|
|
|
|
All three arguments are counts of bytes. The correct type for client
|
|
code to specify counts of bytes is "size_t". (This is not documented;
|
|
it ought to be documented under "mps_class_mv" in the reference manual.
|
|
Note that it is incorrect to use the "Size" type -- this is an
|
|
MPS-internal type.)
|
|
|
|
Finally, note that:
|
|
"mpscmv.h" is an 'external' header file -- part of the interface
|
|
to the MPS, which you #include.
|
|
"poolmv.h" is an 'internal' header file, *not* a client-visible
|
|
MPS header: your code should not (and does not need
|
|
to) #include it.
|
|
|
|
Here is code to create an arena, and create an MV class pool within it:
|
|
|
|
/* Demo File: 04pool_create.c */
|
|
/* Simple C client of the MPS */
|
|
|
|
#include <stdlib.h> /* for malloc */
|
|
#include <stdio.h> /* for printf */
|
|
|
|
#include "mps.h"
|
|
#include "mpsacl.h" /* for mps_arena_class_cl */
|
|
#include "mpscmv.h" /* for mps_class_mv */
|
|
|
|
int main(void)
|
|
{
|
|
void *pBlock = NULL;
|
|
size_t cbBlock = 1024 * 1024;
|
|
mps_arena_t ArenaDemo = NULL;
|
|
mps_pool_t PoolDemo = NULL;
|
|
|
|
mps_res_t res;
|
|
|
|
{
|
|
/* Create arena */
|
|
|
|
pBlock = malloc(cbBlock);
|
|
if(pBlock == NULL) {
|
|
printf("malloc failed!\n");
|
|
exit(1);
|
|
}
|
|
|
|
res = mps_arena_create(
|
|
&ArenaDemo,
|
|
mps_arena_class_cl(),
|
|
cbBlock,
|
|
pBlock
|
|
);
|
|
if (res != MPS_RES_OK) {
|
|
printf("mps_arena_create: failed with res %d.\n", res);
|
|
exit(2);
|
|
}
|
|
}
|
|
|
|
{
|
|
/* Create pool */
|
|
|
|
size_t cbPoolExtend = 1024;
|
|
size_t cbObjectAvg = 32;
|
|
size_t cbPoolMax = 64 * 1024;
|
|
|
|
size_t cbPoolFree = 0;
|
|
|
|
res = mps_pool_create(
|
|
&PoolDemo,
|
|
ArenaDemo,
|
|
mps_class_mv(),
|
|
cbPoolExtend,
|
|
cbObjectAvg,
|
|
cbPoolMax
|
|
);
|
|
if (res != MPS_RES_OK) {
|
|
printf("mps_pool_create: failed with res %d.\n", res);
|
|
exit(2);
|
|
}
|
|
|
|
cbPoolFree = mps_mv_free_size(PoolDemo);
|
|
printf(
|
|
"PoolDemo has %lu bytes free.\n",
|
|
(unsigned long)cbPoolFree
|
|
);
|
|
}
|
|
|
|
/* rest of program */
|
|
{
|
|
printf("Success.\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Compiling and running this produces the possibly surprising result that
|
|
the freshly-created pool has zero (0) bytes free:
|
|
|
|
% gcc 04pool_create.c mps-kit-1.108.2/code/xcppgc/ci/mps.a mps-kit-1.108.2/code/xcppgc/ci/mpsplan.a
|
|
% ./a.out
|
|
% # (should say: "PoolDemo has 0 bytes free.")
|
|
|
|
Pools request chunks of storage from their arena when they need it, and
|
|
return it afterwards. The MV pool class code does not request storage
|
|
until the first object is allocated; this is why PoolDemo has 0 bytes
|
|
free initially: it has not yet claimed memory from the arena.
|
|
|
|
|
|
Allocating some memory
|
|
----------------------
|
|
|
|
Finally, we can now allocate some memory! We use the simple "mps_alloc"
|
|
function, and store the text "hello, world" in the memory, and print it
|
|
out.
|
|
|
|
/* Demo File: 05alloc.c */
|
|
/* Simple C client of the MPS */
|
|
|
|
#include <stdlib.h> /* for malloc */
|
|
#include <stdio.h> /* for printf */
|
|
#include <string.h> /* for strcpy */
|
|
|
|
#include "mps.h"
|
|
#include "mpsacl.h" /* for mps_arena_class_cl */
|
|
#include "mpscmv.h" /* for mps_class_mv */
|
|
|
|
static void reportPoolmv(mps_pool_t Pool)
|
|
{
|
|
size_t cbPoolFree = mps_mv_free_size(Pool);
|
|
printf(
|
|
"Pool has %lu bytes free.\n",
|
|
(unsigned long)cbPoolFree
|
|
);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
void *pBlock = NULL;
|
|
size_t cbBlock = 1024 * 1024;
|
|
mps_arena_t ArenaDemo = NULL;
|
|
mps_pool_t PoolDemo = NULL;
|
|
|
|
mps_res_t res;
|
|
|
|
{
|
|
/* Create arena */
|
|
|
|
pBlock = malloc(cbBlock);
|
|
if(pBlock == NULL) {
|
|
printf("malloc failed!\n");
|
|
exit(1);
|
|
}
|
|
|
|
res = mps_arena_create(
|
|
&ArenaDemo,
|
|
mps_arena_class_cl(),
|
|
cbBlock,
|
|
pBlock
|
|
);
|
|
if (res != MPS_RES_OK) {
|
|
printf("mps_arena_create: failed with res %d.\n", res);
|
|
exit(2);
|
|
}
|
|
}
|
|
|
|
{
|
|
/* Create pool */
|
|
|
|
size_t cbPoolExtend = 1024;
|
|
size_t cbObjectAvg = 32;
|
|
size_t cbPoolMax = 64 * 1024;
|
|
|
|
size_t cbPoolFree = 0;
|
|
|
|
res = mps_pool_create(
|
|
&PoolDemo,
|
|
ArenaDemo,
|
|
mps_class_mv(),
|
|
cbPoolExtend,
|
|
cbObjectAvg,
|
|
cbPoolMax
|
|
);
|
|
if (res != MPS_RES_OK) {
|
|
printf("mps_pool_create: failed with res %d.\n", res);
|
|
exit(2);
|
|
}
|
|
|
|
reportPoolmv(PoolDemo);
|
|
}
|
|
|
|
{
|
|
/* Allocate memory */
|
|
|
|
size_t cbBuffer = 100;
|
|
void *p = NULL;
|
|
|
|
res = mps_alloc(&p, PoolDemo, cbBuffer);
|
|
if (res != MPS_RES_OK) {
|
|
printf("mps_alloc: failed with res %d.\n", res);
|
|
exit(2);
|
|
}
|
|
|
|
reportPoolmv(PoolDemo);
|
|
|
|
{
|
|
/* Show that it really is memory */
|
|
|
|
char *pbBuffer = (char *)p;
|
|
|
|
strcpy(pbBuffer, "hello--world\n");
|
|
pbBuffer[5] = ',';
|
|
pbBuffer[6] = ' ';
|
|
printf(pbBuffer);
|
|
}
|
|
}
|
|
|
|
printf(
|
|
"Success: The hello-world example code successfully allocated\n"
|
|
"some memory using mps_alloc(), in an MV pool, in a CL arena.\n"
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
To compile and run this:
|
|
|
|
% gcc 05alloc.c mps-kit-1.108.2/code/xcppgc/ci/mps.a mps-kit-1.108.2/code/xcppgc/ci/mpsplan.a
|
|
% ./a.out
|
|
% # (should say: "PoolDemo has 0 bytes free.")
|
|
% # (and then: "PoolDemo has NNNN bytes free.", where NNNN depends on platform.)
|
|
% # (and then: "hello, world")
|
|
|
|
That's the end of this example code.
|
|
|
|
|
|
DOCUMENT HISTORY
|
|
|
|
2006-02-17 RHSK Created from scratch, referencing version/1.106.1
|
|
2006-04-10 RHSK Tidy for release; refer to version/1.106.2
|
|
2006-12-13 RHSK Version 1.107
|
|
2007-07-06 RHSK Version 1.108
|
|
2007-12-21 RHSK Release 1.108.1
|
|
2008-05-01 RHSK Release 1.108.2
|
|
|
|
|
|
COPYRIGHT AND LICENSE
|
|
|
|
Copyright (C) 2006,2007 Ravenbrook Limited <http://www.ravenbrook.com/>.
|
|
All rights reserved. This is an open source license. Contact
|
|
Ravenbrook for commercial licensing options.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
3. Redistributions in any form must be accompanied by information on how
|
|
to obtain complete source code for this software and any
|
|
accompanying software that uses this software. The source code must
|
|
either be included in the distribution or be available for no more than
|
|
the cost of distribution plus a nominal fee, and must be freely
|
|
redistributable under reasonable conditions. For an executable file,
|
|
complete source code means the source code for all modules it contains.
|
|
It does not include source code for modules or files that typically
|
|
accompany the major components of the operating system on which the
|
|
executable file runs.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
[END]
|