summaryrefslogtreecommitdiffstats
path: root/Doc/tools/sphinxext/pyspecific.py
blob: e8eb70368a37e80fa217d4c9acabb0ecbbecff80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# -*- coding: utf-8 -*-
"""
    pyspecific.py
    ~~~~~~~~~~~~~

    Sphinx extension with Python doc-specific markup.

    :copyright: 2008, 2009, 2010, 2011, 2012 by Georg Brandl.
    :license: Python license.
"""

ISSUE_URI = 'http://bugs.python.org/issue%s'
SOURCE_URI = 'http://hg.python.org/cpython/file/3.3/%s'

from docutils import nodes, utils
from sphinx.util.nodes import split_explicit_title

# monkey-patch reST parser to disable alphabetic and roman enumerated lists
from docutils.parsers.rst.states import Body
Body.enum.converters['loweralpha'] = \
    Body.enum.converters['upperalpha'] = \
    Body.enum.converters['lowerroman'] = \
    Body.enum.converters['upperroman'] = lambda x: None

# monkey-patch HTML translator to give versionmodified paragraphs a class
def new_visit_versionmodified(self, node):
    self.body.append(self.starttag(node, 'p', CLASS=node['type']))
    text = versionlabels[node['type']] % node['version']
    if len(node):
        text += ':'
    else:
        text += '.'
    self.body.append('<span class="versionmodified">%s</span> ' % text)

from sphinx.writers.html import HTMLTranslator
from sphinx.writers.latex import LaTeXTranslator
from sphinx.locale import versionlabels
HTMLTranslator.visit_versionmodified = new_visit_versionmodified
HTMLTranslator.visit_versionmodified = new_visit_versionmodified

# monkey-patch HTML and LaTeX translators to keep doctest blocks in the
# doctest docs themselves
orig_visit_literal_block = HTMLTranslator.visit_literal_block
def new_visit_literal_block(self, node):
    meta = self.builder.env.metadata[self.builder.current_docname]
    old_trim_doctest_flags = self.highlighter.trim_doctest_flags
    if 'keepdoctest' in meta:
        self.highlighter.trim_doctest_flags = False
    try:
        orig_visit_literal_block(self, node)
    finally:
        self.highlighter.trim_doctest_flags = old_trim_doctest_flags

HTMLTranslator.visit_literal_block = new_visit_literal_block

orig_depart_literal_block = LaTeXTranslator.depart_literal_block
def new_depart_literal_block(self, node):
    meta = self.builder.env.metadata[self.curfilestack[-1]]
    old_trim_doctest_flags = self.highlighter.trim_doctest_flags
    if 'keepdoctest' in meta:
        self.highlighter.trim_doctest_flags = False
    try:
        orig_depart_literal_block(self, node)
    finally:
        self.highlighter.trim_doctest_flags = old_trim_doctest_flags

LaTeXTranslator.depart_literal_block = new_depart_literal_block

# Support for marking up and linking to bugs.python.org issues

def issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
    issue = utils.unescape(text)
    text = 'issue ' + issue
    refnode = nodes.reference(text, text, refuri=ISSUE_URI % issue)
    return [refnode], []


# Support for linking to Python source files easily

def source_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
    has_t, title, target = split_explicit_title(text)
    title = utils.unescape(title)
    target = utils.unescape(target)
    refnode = nodes.reference(title, title, refuri=SOURCE_URI % target)
    return [refnode], []


# Support for marking up implementation details

from sphinx.util.compat import Directive

class ImplementationDetail(Directive):

    has_content = True
    required_arguments = 0
    optional_arguments = 1
    final_argument_whitespace = True

    def run(self):
        pnode = nodes.compound(classes=['impl-detail'])
        content = self.content
        add_text = nodes.strong('CPython implementation detail:',
                                'CPython implementation detail:')
        if self.arguments:
            n, m = self.state.inline_text(self.arguments[0], self.lineno)
            pnode.append(nodes.paragraph('', '', *(n + m)))
        self.state.nested_parse(content, self.content_offset, pnode)
        if pnode.children and isinstance(pnode[0], nodes.paragraph):
            pnode[0].insert(0, add_text)
            pnode[0].insert(1, nodes.Text(' '))
        else:
            pnode.insert(0, nodes.paragraph('', '', add_text))
        return [pnode]


# Support for documenting decorators

from sphinx import addnodes
from sphinx.domains.python import PyModulelevel, PyClassmember

class PyDecoratorMixin(object):
    def handle_signature(self, sig, signode):
        ret = super(PyDecoratorMixin, self).handle_signature(sig, signode)
        signode.insert(0, addnodes.desc_addname('@', '@'))
        return ret

    def needs_arglist(self):
        return False

class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel):
    def run(self):
        # a decorator function is a function after all
        self.name = 'py:function'
        return PyModulelevel.run(self)

class PyDecoratorMethod(PyDecoratorMixin, PyClassmember):
    def run(self):
        self.name = 'py:method'
        return PyClassmember.run(self)


# Support for documenting version of removal in deprecations

from sphinx.locale import versionlabels
from sphinx.util.compat import Directive

versionlabels['deprecated-removed'] = \
    'Deprecated since version %s, will be removed in version %s'

class DeprecatedRemoved(Directive):
    has_content = True
    required_arguments = 2
    optional_arguments = 1
    final_argument_whitespace = True
    option_spec = {}

    def run(self):
        node = addnodes.versionmodified()
        node.document = self.state.document
        node['type'] = 'deprecated-removed'
        version = (self.arguments[0], self.arguments[1])
        node['version'] = version
        if len(self.arguments) == 3:
            inodes, messages = self.state.inline_text(self.arguments[2],
                                                      self.lineno+1)
            node.extend(inodes)
            if self.content:
                self.state.nested_parse(self.content, self.content_offset, node)
            ret = [node] + messages
        else:
            ret = [node]
        env = self.state.document.settings.env
        env.note_versionchange('deprecated', version[0], node, self.lineno)
        return ret


# Support for including Misc/NEWS

import re
import codecs

issue_re = re.compile('([Ii])ssue #([0-9]+)')
whatsnew_re = re.compile(r"(?im)^what's new in (.*?)\??$")

class MiscNews(Directive):
    has_content = False
    required_arguments = 1
    optional_arguments = 0
    final_argument_whitespace = False
    option_spec = {}

    def run(self):
        fname = self.arguments[0]
        source = self.state_machine.input_lines.source(
            self.lineno - self.state_machine.input_offset - 1)
        source_dir = path.dirname(path.abspath(source))
        fpath = path.join(source_dir, fname)
        self.state.document.settings.record_dependencies.add(fpath)
        try:
            fp = codecs.open(fpath, encoding='utf-8')
            try:
                content = fp.read()
            finally:
                fp.close()
        except Exception:
            text = 'The NEWS file is not available.'
            node = nodes.strong(text, text)
            return [node]
        content = issue_re.sub(r'`\1ssue #\2 <http://bugs.python.org/\2>`__',
                               content)
        content = whatsnew_re.sub(r'\1', content)
        # remove first 3 lines as they are the main heading
        lines = ['.. default-role:: obj', ''] + content.splitlines()[3:]
        self.state_machine.insert_input(lines, fname)
        return []


# Support for building "topic help" for pydoc

pydoc_topic_labels = [
    'assert', 'assignment', 'atom-identifiers', 'atom-literals',
    'attribute-access', 'attribute-references', 'augassign', 'binary',
    'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object',
    'bltin-null-object', 'bltin-type-objects', 'booleans',
    'break', 'callable-types', 'calls', 'class', 'comparisons', 'compound',
    'context-managers', 'continue', 'conversions', 'customization', 'debugger',
    'del', 'dict', 'dynamic-features', 'else', 'exceptions', 'execmodel',
    'exprlists', 'floating', 'for', 'formatstrings', 'function', 'global',
    'id-classes', 'identifiers', 'if', 'imaginary', 'import', 'in', 'integers',
    'lambda', 'lists', 'naming', 'nonlocal', 'numbers', 'numeric-types',
    'objects', 'operator-summary', 'pass', 'power', 'raise', 'return',
    'sequence-types', 'shifting', 'slicings', 'specialattrs', 'specialnames',
    'string-methods', 'strings', 'subscriptions', 'truth', 'try', 'types',
    'typesfunctions', 'typesmapping', 'typesmethods', 'typesmodules',
    'typesseq', 'typesseq-mutable', 'unary', 'while', 'with', 'yield'
]

from os import path
from time import asctime
from pprint import pformat
from docutils.io import StringOutput
from docutils.utils import new_document

from sphinx.builders import Builder
from sphinx.writers.text import TextWriter


class PydocTopicsBuilder(Builder):
    name = 'pydoc-topics'

    def init(self):
        self.topics = {}

    def get_outdated_docs(self):
        return 'all pydoc topics'

    def get_target_uri(self, docname, typ=None):
        return ''  # no URIs

    def write(self, *ignored):
        writer = TextWriter(self)
        for label in self.status_iterator(pydoc_topic_labels,
                                          'building topics... ',
                                          length=len(pydoc_topic_labels)):
            if label not in self.env.domaindata['std']['labels']:
                self.warn('label %r not in documentation' % label)
                continue
            docname, labelid, sectname = self.env.domaindata['std']['labels'][label]
            doctree = self.env.get_and_resolve_doctree(docname, self)
            document = new_document('<section node>')
            document.append(doctree.ids[labelid])
            destination = StringOutput(encoding='utf-8')
            writer.write(document, destination)
            self.topics[label] = writer.output.encode('utf-8')

    def finish(self):
        f = open(path.join(self.outdir, 'topics.py'), 'w')
        try:
            f.write('# -*- coding: utf-8 -*-\n')
            f.write('# Autogenerated by Sphinx on %s\n' % asctime())
            f.write('topics = ' + pformat(self.topics) + '\n')
        finally:
            f.close()


# Support for checking for suspicious markup

import suspicious


# Support for documenting Opcodes

import re

opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?')

def parse_opcode_signature(env, sig, signode):
    """Transform an opcode signature into RST nodes."""
    m = opcode_sig_re.match(sig)
    if m is None:
        raise ValueError
    opname, arglist = m.groups()
    signode += addnodes.desc_name(opname, opname)
    if arglist is not None:
        paramlist = addnodes.desc_parameterlist()
        signode += paramlist
        paramlist += addnodes.desc_parameter(arglist, arglist)
    return opname.strip()


# Support for documenting pdb commands

pdbcmd_sig_re = re.compile(r'([a-z()!]+)\s*(.*)')

# later...
#pdbargs_tokens_re = re.compile(r'''[a-zA-Z]+  |  # identifiers
#                                   [.,:]+     |  # punctuation
#                                   [\[\]()]   |  # parens
#                                   \s+           # whitespace
#                                   ''', re.X)

def parse_pdb_command(env, sig, signode):
    """Transform a pdb command signature into RST nodes."""
    m = pdbcmd_sig_re.match(sig)
    if m is None:
        raise ValueError
    name, args = m.groups()
    fullname = name.replace('(', '').replace(')', '')
    signode += addnodes.desc_name(name, name)
    if args:
        signode += addnodes.desc_addname(' '+args, ' '+args)
    return fullname


def setup(app):
    app.add_role('issue', issue_role)
    app.add_role('source', source_role)
    app.add_directive('impl-detail', ImplementationDetail)
    app.add_directive('deprecated-removed', DeprecatedRemoved)
    app.add_builder(PydocTopicsBuilder)
    app.add_builder(suspicious.CheckSuspiciousMarkupBuilder)
    app.add_description_unit('opcode', 'opcode', '%s (opcode)',
                             parse_opcode_signature)
    app.add_description_unit('pdbcommand', 'pdbcmd', '%s (pdb command)',
                             parse_pdb_command)
    app.add_description_unit('2to3fixer', '2to3fixer', '%s (2to3 fixer)')
    app.add_directive_to_domain('py', 'decorator', PyDecoratorFunction)
    app.add_directive_to_domain('py', 'decoratormethod', PyDecoratorMethod)
    app.add_directive('miscnews', MiscNews)
can reduce the size of the heap in memory by * eliminating free blocks at the tail of the buffer before flushing the * buffer out. */ if(heap->freelist) { H5HL_free_t *tmp_fl; H5HL_free_t *last_fl = NULL; /* Search for a free block at the end of the buffer */ for(tmp_fl = heap->freelist; tmp_fl; tmp_fl = tmp_fl->next) /* Check if the end of this free block is at the end of the buffer */ if(tmp_fl->offset + tmp_fl->size == heap->dblk_size) { last_fl = tmp_fl; break; } /* end if */ /* * Found free block at the end of the buffer, decide what to do * about it */ if(last_fl) { /* * If the last free block's size is more than half the memory * buffer size (and the memory buffer is larger than the * minimum size), reduce or eliminate it. */ if(last_fl->size >= (heap->dblk_size / 2) && heap->dblk_size > H5HL_MIN_HEAP) { /* * Reduce size of buffer until it's too small or would * eliminate the free block */ while(new_heap_size > H5HL_MIN_HEAP && new_heap_size >= (last_fl->offset + H5HL_SIZEOF_FREE(f))) new_heap_size /= 2; /* * Check if reducing the memory buffer size would * eliminate the free block */ if(new_heap_size < (last_fl->offset + H5HL_SIZEOF_FREE(f))) { /* Check if this is the only block on the free list */ if(last_fl->prev == NULL && last_fl->next == NULL) { /* Double the new memory size */ new_heap_size *= 2; /* Truncate the free block */ last_fl->size = H5HL_ALIGN(new_heap_size - last_fl->offset); new_heap_size = last_fl->offset + last_fl->size; HDassert(last_fl->size >= H5HL_SIZEOF_FREE(f)); } /* end if */ else { /* * Set the size of the memory buffer to the start * of the free list */ new_heap_size = last_fl->offset; /* Eliminate the free block from the list */ last_fl = H5HL__remove_free(heap, last_fl); } /* end else */ } /* end if */ else { /* Truncate the free block */ last_fl->size = H5HL_ALIGN(new_heap_size - last_fl->offset); new_heap_size = last_fl->offset + last_fl->size; HDassert(last_fl->size >= H5HL_SIZEOF_FREE(f)); HDassert(last_fl->size == H5HL_ALIGN(last_fl->size)); } /* end else */ } /* end if */ } /* end if */ } /* end if */ /* * If the heap grew smaller than disk storage then move the * data segment of the heap to another contiguous block of disk * storage. */ if(new_heap_size != heap->dblk_size) { HDassert(new_heap_size < heap->dblk_size); /* Resize the memory buffer */ if(NULL == (heap->dblk_image = H5FL_BLK_REALLOC(lheap_chunk, heap->dblk_image, new_heap_size))) H5E_THROW(H5E_CANTALLOC, "memory allocation failed"); /* Reallocate data block in file */ if(FAIL == H5HL__dblk_realloc(f, heap, new_heap_size)) H5E_THROW(H5E_CANTRESIZE, "reallocating data block failed"); } /* end if */ CATCH /* No special processing on errors */ END_FUNC(STATIC) /* H5HL__minimize_heap_space() */ /*------------------------------------------------------------------------- * Function: H5HL_protect * * Purpose: This function is a wrapper for the H5AC_protect call. * * Return: Success: Non-NULL pointer to the local heap prefix. * Failure: NULL * * Programmer: Bill Wendling * Sept. 17, 2003 * *------------------------------------------------------------------------- */ BEGIN_FUNC(PRIV, ERR, H5HL_t *, NULL, NULL, H5HL_protect(H5F_t *f, haddr_t addr, unsigned flags)) H5HL_cache_prfx_ud_t prfx_udata; /* User data for protecting local heap prefix */ H5HL_prfx_t *prfx = NULL; /* Local heap prefix */ H5HL_dblk_t *dblk = NULL; /* Local heap data block */ H5HL_t *heap = NULL; /* Heap data structure */ unsigned prfx_cache_flags = H5AC__NO_FLAGS_SET; /* Cache flags for unprotecting prefix entry */ unsigned dblk_cache_flags = H5AC__NO_FLAGS_SET; /* Cache flags for unprotecting data block entry */ /* check arguments */ HDassert(f); HDassert(H5F_addr_defined(addr)); /* only the H5AC__READ_ONLY_FLAG may appear in flags */ HDassert((flags & (unsigned)(~H5AC__READ_ONLY_FLAG)) == 0); /* Construct the user data for protect callback */ prfx_udata.sizeof_size = H5F_SIZEOF_SIZE(f); prfx_udata.sizeof_addr = H5F_SIZEOF_ADDR(f); prfx_udata.prfx_addr = addr; prfx_udata.sizeof_prfx = H5HL_SIZEOF_HDR(f); /* Protect the local heap prefix */ if(NULL == (prfx = (H5HL_prfx_t *)H5AC_protect(f, H5AC_LHEAP_PRFX, addr, &prfx_udata, flags))) H5E_THROW(H5E_CANTPROTECT, "unable to load heap prefix"); /* Get the pointer to the heap */ heap = prfx->heap; /* Check if the heap is already pinned in memory */ /* (for re-entrant situation) */ if(heap->prots == 0) { /* Check if heap has separate data block */ if(heap->single_cache_obj) /* Set the flag for pinning the prefix when unprotecting it */ prfx_cache_flags |= H5AC__PIN_ENTRY_FLAG; else { /* Protect the local heap data block */ if(NULL == (dblk = (H5HL_dblk_t *)H5AC_protect(f, H5AC_LHEAP_DBLK, heap->dblk_addr, heap, flags))) H5E_THROW(H5E_CANTPROTECT, "unable to load heap data block"); /* Set the flag for pinning the data block when unprotecting it */ dblk_cache_flags |= H5AC__PIN_ENTRY_FLAG; } /* end if */ } /* end if */ /* Increment # of times heap is protected */ heap->prots++; /* Set return value */ ret_value = heap; CATCH /* Release the prefix from the cache, now pinned */ if(prfx && heap && H5AC_unprotect(f, H5AC_LHEAP_PRFX, heap->prfx_addr, prfx, prfx_cache_flags) < 0) H5E_THROW(H5E_CANTUNPROTECT, "unable to release local heap prefix"); /* Release the data block from the cache, now pinned */ if(dblk && heap && H5AC_unprotect(f, H5AC_LHEAP_DBLK, heap->dblk_addr, dblk, dblk_cache_flags) < 0) H5E_THROW(H5E_CANTUNPROTECT, "unable to release local heap data block"); END_FUNC(PRIV) /* end H5HL_protect() */ /*------------------------------------------------------------------------- * Function: H5HL_offset_into * * Purpose: Called directly after the call to H5HL_protect so that * a pointer to the object in the heap can be obtained. * * Return: Success: Valid pointer. * Failure: Can't fail * * Programmer: Bill Wendling * Sept. 17, 2003 * *------------------------------------------------------------------------- */ BEGIN_FUNC(PRIV, ERR, void *, NULL, NULL, H5HL_offset_into(const H5HL_t *heap, size_t offset)) /* Sanity check */ HDassert(heap); if(offset >= heap->dblk_size) H5E_THROW(H5E_CANTGET, "unable to offset into local heap data block"); ret_value = heap->dblk_image + offset; CATCH /* No special processing on errors */ END_FUNC(PRIV) /* end H5HL_offset_into() */ /*------------------------------------------------------------------------- * Function: H5HL_unprotect * * Purpose: Unprotect the data retrieved by the H5HL_protect call. * * Return: SUCCEED/FAIL * * Programmer: Bill Wendling * Sept. 17, 2003 * *------------------------------------------------------------------------- */ BEGIN_FUNC(PRIV, ERR, herr_t, SUCCEED, FAIL, H5HL_unprotect(H5HL_t *heap)) /* check arguments */ HDassert(heap); /* Decrement # of times heap is protected */ heap->prots--; /* Check for last unprotection of heap */ if(heap->prots == 0) { /* Check for separate heap data block */ if(heap->single_cache_obj) { /* Mark local heap prefix as evictable again */ if(FAIL == H5AC_unpin_entry(heap->prfx)) H5E_THROW(H5E_CANTUNPIN, "unable to unpin local heap data block"); } /* end if */ else { /* Sanity check */ HDassert(heap->dblk); /* Mark local heap data block as evictable again */ /* (data block still pins prefix) */ if(FAIL == H5AC_unpin_entry(heap->dblk)) H5E_THROW(H5E_CANTUNPIN, "unable to unpin local heap data block"); } /* end else */ } /* end if */ CATCH /* No special processing on errors */ END_FUNC(PRIV) /* end H5HL_unprotect() */ /*------------------------------------------------------------------------- * Function: H5HL__remove_free * * Purpose: Removes free list element FL from the specified heap and * frees it. * * Return: NULL * * Programmer: Robb Matzke * Jul 17 1997 * *------------------------------------------------------------------------- */ BEGIN_FUNC(STATIC, NOERR, H5HL_free_t *, NULL, -, H5HL__remove_free(H5HL_t *heap, H5HL_free_t *fl)) if(fl->prev) fl->prev->next = fl->next; if(fl->next) fl->next->prev = fl->prev; if(!fl->prev) heap->freelist = fl->next; /* H5FL_FREE always returns NULL so we can't check for errors */ ret_value = (H5HL_free_t *)H5FL_FREE(H5HL_free_t, fl); END_FUNC(STATIC) /* end H5HL__remove_free() */ /*------------------------------------------------------------------------- * Function: H5HL__dirty * * Purpose: Mark heap as dirty * * Return: SUCCEED/FAIL * * Programmer: Quincey Koziol * Oct 12 2008 * *------------------------------------------------------------------------- */ BEGIN_FUNC(STATIC, ERR, herr_t, SUCCEED, FAIL, H5HL__dirty(H5HL_t *heap)) /* check arguments */ HDassert(heap); HDassert(heap->prfx); /* Mark heap data block as dirty, if there is one */ if(!heap->single_cache_obj) { /* Sanity check */ HDassert(heap->dblk); if(FAIL == H5AC_mark_entry_dirty(heap->dblk)) H5E_THROW(H5E_CANTMARKDIRTY, "unable to mark heap data block as dirty"); } /* end if */ /* Mark heap prefix as dirty */ if(FAIL == H5AC_mark_entry_dirty(heap->prfx)) H5E_THROW(H5E_CANTMARKDIRTY, "unable to mark heap prefix as dirty"); CATCH /* No special processing on errors */ END_FUNC(STATIC) /* end H5HL__dirty() */ /*------------------------------------------------------------------------- * Function: H5HL_insert * * Purpose: Inserts a new item into the heap. * * Return: Success: Offset of new item within heap. * Failure: UFAIL * * Programmer: Robb Matzke * Jul 17 1997 * *------------------------------------------------------------------------- */ BEGIN_FUNC(PRIV, ERR, size_t, UFAIL, UFAIL, H5HL_insert(H5F_t *f, H5HL_t *heap, size_t buf_size, const void *buf)) H5HL_free_t *fl = NULL, *last_fl = NULL; size_t offset = 0; size_t need_size; hbool_t found; /* check arguments */ HDassert(f); HDassert(heap); HDassert(buf_size > 0); HDassert(buf); /* Mark heap as dirty in cache */ /* (A bit early in the process, but it's difficult to determine in the * code below where to mark the heap as dirty, especially in error cases, * so we just accept that an extra flush of the heap info could occur * if an error occurs -QAK) */ if(FAIL == H5HL__dirty(heap)) H5E_THROW(H5E_CANTMARKDIRTY, "unable to mark heap as dirty"); /* * In order to keep the free list descriptors aligned on word boundaries, * whatever that might mean, we round the size up to the next multiple of * a word. */ need_size = H5HL_ALIGN(buf_size); /* * Look for a free slot large enough for this object and which would * leave zero or at least H5G_SIZEOF_FREE bytes left over. */ for(fl = heap->freelist, found = FALSE; fl; fl = fl->next) { if(fl->size > need_size && fl->size - need_size >= H5HL_SIZEOF_FREE(f)) { /* a big enough free block was found */ offset = fl->offset; fl->offset += need_size; fl->size -= need_size; HDassert(fl->offset == H5HL_ALIGN(fl->offset)); HDassert(fl->size == H5HL_ALIGN(fl->size)); found = TRUE; break; } else if(fl->size == need_size) { /* free block of exact size found */ offset = fl->offset; fl = H5HL__remove_free(heap, fl); found = TRUE; break; } else if(!last_fl || last_fl->offset < fl->offset) { /* track free space that's closest to end of heap */ last_fl = fl; } } /* end for */ /* * If no free chunk was large enough, then allocate more space and * add it to the free list. If the heap ends with a free chunk, we * can extend that free chunk. Otherwise we'll have to make another * free chunk. If the heap must expand, we double its size. */ if(found == FALSE) { size_t need_more; /* How much more space we need */ size_t new_dblk_size; /* Final size of space allocated for heap data block */ size_t old_dblk_size; /* Previous size of space allocated for heap data block */ htri_t was_extended; /* Whether the local heap's data segment on disk was extended */ /* At least double the heap's size, making certain there's enough room * for the new object */ need_more = MAX(need_size, heap->dblk_size); /* If there is no last free block or it's not at the end of the heap, * and the amount of space to allocate is not big enough to include at * least the new object and a free-list info, trim down the amount of * space requested to just the amount of space needed. (Generally * speaking, this only occurs when the heap is small -QAK) */ if(!(last_fl && last_fl->offset + last_fl->size == heap->dblk_size) && (need_more < (need_size + H5HL_SIZEOF_FREE(f)))) need_more = need_size; new_dblk_size = heap->dblk_size + need_more; HDassert(heap->dblk_size < new_dblk_size); old_dblk_size = heap->dblk_size; H5_CHECK_OVERFLOW(heap->dblk_size, size_t, hsize_t); H5_CHECK_OVERFLOW(new_dblk_size, size_t, hsize_t); /* Extend current heap if possible */ was_extended = H5MF_try_extend(f, H5FD_MEM_LHEAP, heap->dblk_addr, (hsize_t)(heap->dblk_size), (hsize_t)need_more); if(FAIL == was_extended) H5E_THROW(H5E_CANTEXTEND, "error trying to extend heap"); /* Check if we extended the heap data block in file */ if(was_extended == TRUE) { /* Check for prefix & data block contiguous */ if(heap->single_cache_obj) { /* Resize prefix+data block */ if(FAIL == H5AC_resize_entry(heap->prfx, (size_t)(heap->prfx_size + new_dblk_size))) H5E_THROW(H5E_CANTRESIZE, "unable to resize heap prefix in cache"); } /* end if */ else { /* Resize 'standalone' data block */ if(FAIL == H5AC_resize_entry(heap->dblk, (size_t)new_dblk_size)) H5E_THROW(H5E_CANTRESIZE, "unable to resize heap data block in cache"); } /* end else */ /* Note new size */ heap->dblk_size = new_dblk_size; } /* end if */ else { /* ...if we can't, allocate a new chunk & release the old */ /* Reallocate data block in file */ if(FAIL == H5HL__dblk_realloc(f, heap, new_dblk_size)) H5E_THROW(H5E_CANTRESIZE, "reallocating data block failed"); } /* end if */ /* If the last free list in the heap is at the end of the heap, extend it */ if(last_fl && last_fl->offset + last_fl->size == old_dblk_size) { /* * Increase the size of the last free block. */ offset = last_fl->offset; last_fl->offset += need_size; last_fl->size += need_more - need_size; HDassert(last_fl->offset == H5HL_ALIGN(last_fl->offset)); HDassert(last_fl->size == H5HL_ALIGN(last_fl->size)); if (last_fl->size < H5HL_SIZEOF_FREE(f)) { #ifdef H5HL_DEBUG if (H5DEBUG(HL) && last_fl->size) { HDfprintf(H5DEBUG(HL), "H5HL: lost %lu bytes at line %d\n", (unsigned long)(last_fl->size), __LINE__); } #endif last_fl = H5HL__remove_free(heap, last_fl); } } /* end if */ else { /* * Create a new free list element large enough that we can * take some space out of it right away. */ offset = old_dblk_size; if(need_more - need_size >= H5HL_SIZEOF_FREE(f)) { if(NULL == (fl = H5FL_MALLOC(H5HL_free_t))) H5E_THROW(H5E_CANTALLOC, "memory allocation failed"); fl->offset = old_dblk_size + need_size; fl->size = need_more - need_size; HDassert(fl->offset == H5HL_ALIGN(fl->offset)); HDassert(fl->size == H5HL_ALIGN(fl->size)); fl->prev = NULL; fl->next = heap->freelist; if(heap->freelist) heap->freelist->prev = fl; heap->freelist = fl; #ifdef H5HL_DEBUG } else if (H5DEBUG(HL) && need_more > need_size) { HDfprintf(H5DEBUG(HL), "H5HL_insert: lost %lu bytes at line %d\n", (unsigned long)(need_more - need_size), __LINE__); #endif } } /* end else */ #ifdef H5HL_DEBUG if (H5DEBUG(HL)) { HDfprintf(H5DEBUG(HL), "H5HL: resize mem buf from %lu to %lu bytes\n", (unsigned long)(old_dblk_size), (unsigned long)(old_dblk_size + need_more)); } #endif if(NULL == (heap->dblk_image = H5FL_BLK_REALLOC(lheap_chunk, heap->dblk_image, heap->dblk_size))) H5E_THROW(H5E_CANTALLOC, "memory allocation failed"); /* Clear new section so junk doesn't appear in the file */ /* (Avoid clearing section which will be overwritten with newly inserted data) */ HDmemset(heap->dblk_image + offset + buf_size, 0, (new_dblk_size - (offset + buf_size))); } /* end if */ /* Copy the data into the heap */ HDmemcpy(heap->dblk_image + offset, buf, buf_size); /* Set return value */ ret_value = offset; CATCH /* No special processing on errors */ END_FUNC(PRIV) /* H5HL_insert() */ /*------------------------------------------------------------------------- * Function: H5HL_remove * * Purpose: Removes an object or part of an object from the heap at * address ADDR of file F. The object (or part) to remove * begins at byte OFFSET from the beginning of the heap and * continues for SIZE bytes. * * Once part of an object is removed, one must not attempt * to access that part. Removing the beginning of an object * results in the object OFFSET increasing by the amount * truncated. Removing the end of an object results in * object truncation. Removing the middle of an object results * in two separate objects, one at the original offset and * one at the first offset past the removed portion. * * Return: SUCCEED/FAIL * * Programmer: Robb Matzke * Jul 16 1997 * *------------------------------------------------------------------------- */ BEGIN_FUNC(PRIV, ERR, herr_t, SUCCEED, FAIL, H5HL_remove(H5F_t *f, H5HL_t *heap, size_t offset, size_t size)) H5HL_free_t *fl = NULL; /* check arguments */ HDassert(f); HDassert(heap); HDassert(size > 0); HDassert(offset == H5HL_ALIGN(offset)); size = H5HL_ALIGN(size); HDassert(offset < heap->dblk_size); HDassert(offset + size <= heap->dblk_size); /* Mark heap as dirty in cache */ /* (A bit early in the process, but it's difficult to determine in the * code below where to mark the heap as dirty, especially in error cases, * so we just accept that an extra flush of the heap info could occur * if an error occurs -QAK) */ if(FAIL == H5HL__dirty(heap)) H5E_THROW(H5E_CANTMARKDIRTY, "unable to mark heap as dirty"); /* * Check if this chunk can be prepended or appended to an already * free chunk. It might also fall between two chunks in such a way * that all three chunks can be combined into one. */ fl = heap->freelist; while(fl) { H5HL_free_t *fl2 = NULL; if((offset + size) == fl->offset) { fl->offset = offset; fl->size += size; HDassert(fl->offset == H5HL_ALIGN(fl->offset)); HDassert(fl->size == H5HL_ALIGN(fl->size)); fl2 = fl->next; while(fl2) { if((fl2->offset + fl2->size) == fl->offset) { fl->offset = fl2->offset; fl->size += fl2->size; HDassert(fl->offset == H5HL_ALIGN(fl->offset)); HDassert(fl->size == H5HL_ALIGN(fl->size)); fl2 = H5HL__remove_free(heap, fl2); if(((fl->offset + fl->size) == heap->dblk_size) && ((2 * fl->size) > heap->dblk_size)) { if(FAIL == H5HL__minimize_heap_space(f, heap)) H5E_THROW(H5E_CANTFREE, "heap size minimization failed"); } H5_LEAVE(SUCCEED); } fl2 = fl2->next; } if(((fl->offset + fl->size) == heap->dblk_size) && ((2 * fl->size) > heap->dblk_size)) { if(FAIL == H5HL__minimize_heap_space(f, heap)) H5E_THROW(H5E_CANTFREE, "heap size minimization failed"); } H5_LEAVE(SUCCEED); } else if(fl->offset + fl->size == offset) { fl->size += size; fl2 = fl->next; HDassert(fl->size == H5HL_ALIGN(fl->size)); while(fl2) { if(fl->offset + fl->size == fl2->offset) { fl->size += fl2->size; HDassert(fl->size == H5HL_ALIGN(fl->size)); fl2 = H5HL__remove_free(heap, fl2); if(((fl->offset + fl->size) == heap->dblk_size) && ((2 * fl->size) > heap->dblk_size)) { if(FAIL == H5HL__minimize_heap_space(f, heap)) H5E_THROW(H5E_CANTFREE, "heap size minimization failed"); } /* end if */ H5_LEAVE(SUCCEED); } /* end if */ fl2 = fl2->next; } /* end while */ if(((fl->offset + fl->size) == heap->dblk_size) && ((2 * fl->size) > heap->dblk_size)) { if(FAIL == H5HL__minimize_heap_space(f, heap)) H5E_THROW(H5E_CANTFREE, "heap size minimization failed"); } /* end if */ H5_LEAVE(SUCCEED); } /* end if */ fl = fl->next; } /* end while */ /* * The amount which is being removed must be large enough to * hold the free list data. If not, the freed chunk is forever * lost. */ if(size < H5HL_SIZEOF_FREE(f)) { #ifdef H5HL_DEBUG if(H5DEBUG(HL)) { HDfprintf(H5DEBUG(HL), "H5HL: lost %lu bytes\n", (unsigned long) size); } #endif H5_LEAVE(SUCCEED); } /* end if */ /* * Add an entry to the free list. */ if(NULL == (fl = H5FL_MALLOC(H5HL_free_t))) H5E_THROW(H5E_CANTALLOC, "memory allocation failed"); fl->offset = offset; fl->size = size; HDassert(fl->offset == H5HL_ALIGN(fl->offset)); HDassert(fl->size == H5HL_ALIGN(fl->size)); fl->prev = NULL; fl->next = heap->freelist; if(heap->freelist) heap->freelist->prev = fl; heap->freelist = fl; if(((fl->offset + fl->size) == heap->dblk_size) && ((2 * fl->size) > heap->dblk_size)) if(FAIL == H5HL__minimize_heap_space(f, heap)) H5E_THROW(H5E_CANTFREE, "heap size minimization failed"); CATCH /* No special processing on exit */ END_FUNC(PRIV) /* end H5HL_remove() */ /*------------------------------------------------------------------------- * Function: H5HL_delete * * Purpose: Deletes a local heap from disk, freeing disk space used. * * Return: SUCCEED/FAIL * * Programmer: Quincey Koziol * Mar 22 2003 * *------------------------------------------------------------------------- */ BEGIN_FUNC(PRIV, ERR, herr_t, SUCCEED, FAIL, H5HL_delete(H5F_t *f, haddr_t addr)) H5HL_t *heap = NULL; /* Local heap to delete */ H5HL_cache_prfx_ud_t prfx_udata; /* User data for protecting local heap prefix */ H5HL_prfx_t *prfx = NULL; /* Local heap prefix */ H5HL_dblk_t *dblk = NULL; /* Local heap data block */ unsigned cache_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting heap */ /* check arguments */ HDassert(f); HDassert(H5F_addr_defined(addr)); /* Construct the user data for protect callback */ prfx_udata.sizeof_size = H5F_SIZEOF_SIZE(f); prfx_udata.sizeof_addr = H5F_SIZEOF_ADDR(f); prfx_udata.prfx_addr = addr; prfx_udata.sizeof_prfx = H5HL_SIZEOF_HDR(f); /* Protect the local heap prefix */ if(NULL == (prfx = (H5HL_prfx_t *)H5AC_protect(f, H5AC_LHEAP_PRFX, addr, &prfx_udata, H5AC__NO_FLAGS_SET))) H5E_THROW(H5E_CANTPROTECT, "unable to load heap prefix"); /* Get the pointer to the heap */ heap = prfx->heap; /* Check if heap has separate data block */ if(!heap->single_cache_obj) /* Protect the local heap data block */ if(NULL == (dblk = (H5HL_dblk_t *)H5AC_protect(f, H5AC_LHEAP_DBLK, heap->dblk_addr, heap, H5AC__NO_FLAGS_SET))) H5E_THROW(H5E_CANTPROTECT, "unable to load heap data block"); /* Set the flags for releasing the prefix and data block */ cache_flags |= H5AC__DIRTIED_FLAG | H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG; CATCH /* Release the data block from the cache, now deleted */ if(dblk && heap && H5AC_unprotect(f, H5AC_LHEAP_DBLK, heap->dblk_addr, dblk, cache_flags) < 0) H5E_THROW(H5E_CANTUNPROTECT, "unable to release local heap data block"); /* Release the prefix from the cache, now deleted */ if(prfx && heap && H5AC_unprotect(f, H5AC_LHEAP_PRFX, heap->prfx_addr, prfx, cache_flags) < 0) H5E_THROW(H5E_CANTUNPROTECT, "unable to release local heap prefix"); END_FUNC(PRIV) /* end H5HL_delete() */ /*------------------------------------------------------------------------- * Function: H5HL_get_size * * Purpose: Retrieves the current size of a heap * * Return: SUCCEED/FAIL * * Programmer: Quincey Koziol * Nov 7 2005 * *------------------------------------------------------------------------- */ BEGIN_FUNC(PRIV, ERR, herr_t, SUCCEED, FAIL, H5HL_get_size(H5F_t *f, haddr_t addr, size_t *size)) H5HL_cache_prfx_ud_t prfx_udata; /* User data for protecting local heap prefix */ H5HL_prfx_t *prfx = NULL; /* Local heap prefix */ H5HL_t *heap; /* Heap data structure */ /* check arguments */ HDassert(f); HDassert(H5F_addr_defined(addr)); HDassert(size); /* Construct the user data for protect callback */ prfx_udata.sizeof_size = H5F_SIZEOF_SIZE(f); prfx_udata.sizeof_addr = H5F_SIZEOF_ADDR(f); prfx_udata.prfx_addr = addr; prfx_udata.sizeof_prfx = H5HL_SIZEOF_HDR(f); /* Protect the local heap prefix */ if(NULL == (prfx = (H5HL_prfx_t *)H5AC_protect(f, H5AC_LHEAP_PRFX, addr, &prfx_udata, H5AC__READ_ONLY_FLAG))) H5E_THROW(H5E_CANTPROTECT, "unable to load heap prefix"); /* Get the pointer to the heap */ heap = prfx->heap; /* Set the size to return */ *size = heap->dblk_size; CATCH if(prfx && FAIL == H5AC_unprotect(f, H5AC_LHEAP_PRFX, heap->prfx_addr, prfx, H5AC__NO_FLAGS_SET)) H5E_THROW(H5E_CANTUNPROTECT, "unable to release local heap prefix"); END_FUNC(PRIV) /* end H5HL_get_size() */ /*------------------------------------------------------------------------- * Function: H5HL_heapsize * * Purpose: Compute the size in bytes of the specified instance of * H5HL_t via H5HL_size() * * Return: SUCCEED/FAIL * * Programmer: Vailin Choi * June 19 2007 * *------------------------------------------------------------------------- */ BEGIN_FUNC(PRIV, ERR, herr_t, SUCCEED, FAIL, H5HL_heapsize(H5F_t *f, haddr_t addr, hsize_t *heap_size)) H5HL_cache_prfx_ud_t prfx_udata; /* User data for protecting local heap prefix */ H5HL_prfx_t *prfx = NULL; /* Local heap prefix */ H5HL_t *heap; /* Heap data structure */ /* check arguments */ HDassert(f); HDassert(H5F_addr_defined(addr)); HDassert(heap_size); /* Construct the user data for protect callback */ prfx_udata.sizeof_size = H5F_SIZEOF_SIZE(f); prfx_udata.sizeof_addr = H5F_SIZEOF_ADDR(f); prfx_udata.prfx_addr = addr; prfx_udata.sizeof_prfx = H5HL_SIZEOF_HDR(f); /* Protect the local heap prefix */ if(NULL == (prfx = (H5HL_prfx_t *)H5AC_protect(f, H5AC_LHEAP_PRFX, addr, &prfx_udata, H5AC__READ_ONLY_FLAG))) H5E_THROW(H5E_CANTPROTECT, "unable to load heap prefix"); /* Get the pointer to the heap */ heap = prfx->heap; /* Accumulate the size of the local heap */ *heap_size += (hsize_t)(heap->prfx_size + heap->dblk_size); CATCH if(prfx && FAIL == H5AC_unprotect(f, H5AC_LHEAP_PRFX, heap->prfx_addr, prfx, H5AC__NO_FLAGS_SET)) H5E_THROW(H5E_CANTUNPROTECT, "unable to release local heap prefix"); END_FUNC(PRIV) /* end H5HL_heapsize() */