source: soft/giet_vm/memo/src/memo.cpp @ 222

Last change on this file since 222 was 215, checked in by karaoui, 12 years ago

New components are now mandotory in the XML description:

The files giet_vsegs.ld and hard_config.h are now autogenerated by the xml2bin tool.

File size: 21.1 KB
RevLine 
[163]1/* -*- c++ -*-
2 *
3 * SOCLIB_LGPL_HEADER_BEGIN
4 *
5 * This file is part of SoCLib, GNU LGPLv2.1.
6 *
7 * SoCLib is free software; you can redistribute vSO and/or modify vSO
8 * under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; version 2.1 of the License.
10 *
11 * SoCLib is distributed in the hope that vSO will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with SoCLib; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301 USA
20 *
21 * SOCLIB_LGPL_HEADER_END
22 *
23 * Copyright (c) UPMC, Lip6, SoC
24 *         Mohamed Lamine KARAOUI <Mohamed.Karaoui@lip6.fr>, 2012
25 */
26
27#include <string.h>
28#include <cassert>
29#include <sstream>
30#include <fstream>
31#include <ios>
32#include <iostream>
33#include <cstdarg>
34#include <cassert>
35#include <iomanip>
36
37
38#include "exception.h"
39#include "memo.h"
40
[215]41//#define MOVER_DEBUG
[163]42
43MeMo::MeMo( const std::string &filename, 
44        const size_t pageSize)
45        :m_path(filename),
46        m_pathHandler(filename),
47        m_ginit(false),
48        m_generator(new elfpp::object())
49{
50   
51    PSeg::setPageSize(pageSize);
52
[173]53    /* loading map_info blob */
54    m_data = std::malloc(bin_size(m_path));
55    if ( !m_data )
56        throw exception::RunTimeError("malloc failed... No memory space");
[163]57
[173]58    m_size = load_bin(m_path, m_data);
59
60    /* checking signature */
61    mapping_header_t*   header = (mapping_header_t*)m_data; 
62    //assert((IN_MAPPING_SIGNATURE == header->signature) and "wrong signature");
63    if((IN_MAPPING_SIGNATURE != header->signature))
64    {
65        std::cout  << "wrong signature " << std::hex <<header->signature << ", should be:"<< IN_MAPPING_SIGNATURE << std::endl;
66        exit(1);
67    }
68   
69   
70
[163]71#ifdef MOVER_DEBUG
72    std::cout << "Binary file path: " << m_path << std::endl;
[165]73    print_mapping_info(m_data);
[163]74#endif
75
76    buildMap(m_data);
77   
78#ifdef MOVER_DEBUG
79    std::cout << "parsing done" << std::endl ;
80#endif
81
82#ifdef MOVER_DEBUG
83    print_mapping();
84    std::cout << *this << std::endl;
85#endif
86
87    m_psegh.check();
88#ifdef MOVER_DEBUG
89    std::cout << "checking done" << std::endl ;
90#endif
91
92}
93
94void MeMo::print( std::ostream &o ) const
95{
96    std::cout << "All sections:" << std::endl;
97    FOREACH( sect, m_generator->get_section_table() )
98    {
99        assert(&*sect != NULL);
100        std::cout << *sect << std::endl; 
101    }
102}
103
104void MeMo::print_mapping() const
105{
106    std::cout << m_psegh << std::endl;
107}
108
109elfpp::section* MeMo::get_sect_by_addr(elfpp::object *loader, unsigned int addr)
110{
111    FOREACH( sect, loader->get_section_table() )
112    {
113        assert(&*sect != NULL);
114        elfpp::sh_flags_e eflags = sect->get_flags();
115        if ( !(eflags & elfpp::SHF_ALLOC) )
116            continue;
117        if(sect->get_load_address() == addr) //load_addr ?
118        {
119            return (&*sect);
120        }
121    }
122    return NULL;
123}
124
125
126void MeMo::buildSoft(const std::string &filename)
127{
128    if(!m_ginit)
[173]129        throw exception::RunTimeError(std::string("Can't get generator! ") );
[163]130
131    m_generator->write(filename);
132
133}
134
[173]135/* Load the content of filename in buffer, and send the size */
136////////////////////////////////////////////////////////
137size_t MeMo::load_bin(std::string filename, void* buffer)
138{
139
140#ifdef MOVER_DEBUG
141    std::cout  << "Trying to load the binary blob from file '" << filename << "'" << std::endl;
142#endif
143
144    std::ifstream input(filename.c_str(), std::ios_base::binary|std::ios_base::in);
145
146    if ( ! input.good() )
147        throw exception::RunTimeError(std::string("Can't open the file: ") + filename);
148
149    input.seekg( 0, std::ifstream::end );
150    size_t size = input.tellg();
151    input.seekg( 0, std::ifstream::beg );
152
153    if ( !buffer )
154        throw exception::RunTimeError("Empty buffer!");
155
156    input.read( (char*)buffer, size );
157
158    input.close();
159
160    return size;
161}
162
163
164/* get the size of the content of the filename */
[165]165//////////////////////////////////////////
[173]166size_t MeMo::bin_size(std::string filename)
[163]167{
[173]168
[163]169#ifdef MOVER_DEBUG
[173]170    std::cout  << "Trying to get the size of the binary blob '" << filename << "'" << std::endl;
[163]171#endif
172
[173]173    std::ifstream input(filename.c_str(), std::ios_base::binary|std::ios_base::in);
[163]174
175    if ( ! input.good() )
[173]176        throw exception::RunTimeError(std::string("Can't open the file: ") + filename);
[163]177
178    input.seekg( 0, std::ifstream::end );
[173]179    size_t size = input.tellg();
[163]180    input.seekg( 0, std::ifstream::beg );
181
[173]182    input.close();
[163]183
[173]184    return size;
[163]185}
186
[173]187
[165]188/////////////
[163]189MeMo::~MeMo()
190{
191    //std::cout << "Deleted MeMo " << *this << std::endl;
[173]192    //std::free(m_data);//should be done by the elfpp driver since we passed the pointer
[163]193    std::map<std::string, elfpp::object*>::iterator l;
194    for(l = m_loaders.begin(); l != m_loaders.end(); l++)
195    {
196        delete (*l).second;
197    }
198    //delete m_generator;
199}
200
201
202/////////////////////////////////////////////////////////////////////////////
203// various mapping_info data structure access functions
204/////////////////////////////////////////////////////////////////////////////
[187]205mapping_cluster_t* MeMo::get_cluster_base( mapping_header_t* header )
206{
207    return   (mapping_cluster_t*) ((char*)header +
208                                  MAPPING_HEADER_SIZE);
209}
[163]210/////////////////////////////////////////////////////////////////////////////
211mapping_pseg_t* MeMo::get_pseg_base( mapping_header_t* header )
212{
213    return   (mapping_pseg_t*)    ((char*)header +
214                                  MAPPING_HEADER_SIZE +
215                                  MAPPING_CLUSTER_SIZE*header->clusters);
216}
217/////////////////////////////////////////////////////////////////////////////
218mapping_vspace_t* MeMo::get_vspace_base( mapping_header_t* header )
219{
220    return   (mapping_vspace_t*)  ((char*)header +
221                                  MAPPING_HEADER_SIZE +
222                                  MAPPING_CLUSTER_SIZE*header->clusters +
223                                  MAPPING_PSEG_SIZE*header->psegs);
224}
225/////////////////////////////////////////////////////////////////////////////
226mapping_vseg_t* MeMo::get_vseg_base( mapping_header_t* header )
227{
228    return   (mapping_vseg_t*)    ((char*)header +
229                                  MAPPING_HEADER_SIZE +
230                                  MAPPING_CLUSTER_SIZE*header->clusters +
231                                  MAPPING_PSEG_SIZE*header->psegs +
232                                  MAPPING_VSPACE_SIZE*header->vspaces);
233}
234/////////////////////////////////////////////////////////////////////////////
235mapping_vobj_t* MeMo::get_vobj_base( mapping_header_t* header )
236{
237    return   (mapping_vobj_t*)    ((char*)header +
238                                  MAPPING_HEADER_SIZE +
239                                  MAPPING_CLUSTER_SIZE*header->clusters +
240                                  MAPPING_PSEG_SIZE*header->psegs +
[165]241                                  MAPPING_VSPACE_SIZE*header->vspaces +
242                                  MAPPING_VSEG_SIZE*header->vsegs);
[163]243}
244
245/////////////////////////////////////////////////////////////////////////////
246// print the content of the mapping_info data structure
247////////////////////////////////////////////////////////////////////////
248void MeMo::print_mapping_info(void* desc)
249{
250    mapping_header_t*   header = (mapping_header_t*)desc; 
251
252    mapping_pseg_t*         pseg    = get_pseg_base( header );;
253    mapping_vspace_t*   vspace  = get_vspace_base ( header );;
254    mapping_vseg_t*         vseg    = get_vseg_base ( header );
255    mapping_vobj_t*         vobj    = get_vobj_base ( header );
256
257    // header
[165]258    std::cout << std::hex << "mapping_info" << std::dec << std::endl
[163]259              << " + signature = " << header->signature << std::endl
[215]260              << " + name      = " << (char*)header->name      << std::endl
[165]261              << " + clusters  = " << header->clusters  << std::endl
262              << " + psegs     = " << header->psegs     << std::endl
263              << " + vspaces   = " << header->vspaces   << std::endl
264              << " + globals   = " << header->globals   << std::endl
265              << " + vsegs     = " << header->vsegs     << std::endl
266              << " + vobjs     = " << header->vsegs     << std::endl
267              << " + tasks     = " << header->tasks     << std::endl;
[163]268
269    // psegs
270    for ( size_t pseg_id = 0 ; pseg_id < header->psegs ; pseg_id++ )
271    {
[165]272        std::cout << "pseg " << pseg[pseg_id].name << std::hex << std::endl
273                  << " + base   = " << pseg[pseg_id].base   << std::endl
274                  << " + length = " << pseg[pseg_id].length << std::endl ;
[163]275    }
276
277    // globals
278    for ( size_t vseg_id = 0 ; vseg_id < header->globals ; vseg_id++ )
279    {
[165]280        std::cout << std::endl;
[215]281        //std::cout << vseg[vseg_id].psegid << std::endl;
[165]282        std::cout << "global vseg "   << vseg[vseg_id].name << std::hex << std::endl
283                  << " + vbase    = " << vseg[vseg_id].vbase << std::endl
284                  << " + length   = " << vseg[vseg_id].length << std::endl
285                  << " + mode     = " << (size_t)vseg[vseg_id].mode << std::endl
286                  << " + ident    = " << (bool)vseg[vseg_id].ident << std::endl
287                  << " + psegname = " << pseg[vseg[vseg_id].psegid].name << std::endl;
288        for( size_t vobj_id = vseg[vseg_id].vobj_offset ; 
289                    vobj_id < vseg[vseg_id].vobj_offset + vseg[vseg_id].vobjs ; 
290                    vobj_id++ )
[163]291        {
[165]292            std::cout << "\t vobj "        << vobj[vobj_id].name    << std::endl
293                      << "\t + index   = " << std::dec << vobj_id   << std::endl
294                      << "\t + type    = " << vobj[vobj_id].type    << std::endl
295                      << "\t + length  = " << vobj[vobj_id].length  << std::endl
296                      << "\t + align   = " << vobj[vobj_id].align   << std::endl
297                      << "\t + binpath = " << vobj[vobj_id].binpath << std::endl
298                      << "\t + init    = " << vobj[vobj_id].init    << std::endl;
[163]299        }
300    }
301
302    // vspaces
303    for ( size_t vspace_id = 0 ; vspace_id < header->vspaces ; vspace_id++ )
304    {
[165]305        std::cout << "***vspace "  << vspace[vspace_id].name   << std::endl
306                  << " + vsegs = " <<  vspace[vspace_id].vsegs << std::endl
307                  << " + vobjs = " <<  vspace[vspace_id].vobjs << std::endl
308                  << " + tasks = " <<  vspace[vspace_id].tasks << std::endl;
[163]309
310        for ( size_t vseg_id = vspace[vspace_id].vseg_offset ; 
311              vseg_id < (vspace[vspace_id].vseg_offset + vspace[vspace_id].vsegs) ; 
312              vseg_id++ )
313        {
[165]314            std::cout << "\t vseg " << vseg[vseg_id].name  << std::endl
315                      << "\t + vbase    = " << vseg[vseg_id].vbase             << std::endl
316                      << "\t + length   = " << vseg[vseg_id].length            << std::endl
317                      << "\t + mode     = " << (size_t)vseg[vseg_id].mode      << std::endl
318                      << "\t + ident    = " << (bool)vseg[vseg_id].ident       << std::endl
319                      << "\t + psegname = " << pseg[vseg[vseg_id].psegid].name << std::endl;
320            for(size_t vobj_id = vseg[vseg_id].vobj_offset ; 
321                       vobj_id < vseg[vseg_id].vobj_offset + vseg[vseg_id].vobjs ; 
322                       vobj_id++ )
[163]323            {
[165]324                std::cout << "\t\t vobj "      << vobj[vobj_id].name      << std::endl
325                          << "\t\t + index   = " << std::dec << vobj_id   << std::endl
326                          << "\t\t + type    = " << vobj[vobj_id].type    << std::endl
327                          << "\t\t + length  = " << vobj[vobj_id].length  << std::endl
328                          << "\t\t + align   = " << vobj[vobj_id].align   << std::endl
329                          << "\t\t + binpath = " << vobj[vobj_id].binpath << std::endl
330                          << "\t\t + init    = " << vobj[vobj_id].init    << std::endl;
[163]331            }
332        }
333
334    }
335} // end print_mapping_info()
336
337
[165]338//////////////////////////////////////////
[173]339void MeMo::vseg_map( mapping_vseg_t* vseg)
[163]340{
[173]341    mapping_vobj_t*     vobj   = get_vobj_base( (mapping_header_t*) m_data );
[163]342    PSeg *ps = &(m_psegh.get(vseg->psegid));// get physical segment pointer(PSegHandler::get)
343    elfpp::section* sect = NULL;
344    size_t cur_vaddr;
345    size_t cur_paddr;
346    bool first = true;
347    bool aligned = false;
348
349    VSeg   *vSO = new VSeg;
350
351    vSO->m_name = std::string(vseg->name);
352    vSO->m_vma = vseg->vbase;
353    vSO->m_lma = ps->nextLma();
354
355    cur_vaddr = vseg->vbase;
356    cur_paddr = ps->nextLma();
357
358    mapping_vobj_t* cur_vobj;
359    size_t simple_size = 0; //for debug
[173]360
[163]361#ifdef MOVER_DEBUG
362    std::cout << "--------------------vseg_map "<< vseg->name <<"---------------------" << std::endl;
363#endif
[173]364
[163]365    for ( size_t vobj_id = vseg->vobj_offset ; vobj_id < (vseg->vobj_offset + vseg->vobjs) ; vobj_id++ )
366    {
367        cur_vobj = &vobj[vobj_id];
368
369#ifdef MOVER_DEBUG
[173]370        std::cout << std::hex << "current vobj("<< vobj_id <<"): " << cur_vobj->name << " (" <<cur_vobj->vaddr << ")"
[163]371                        << " size: "<< cur_vobj->length << " type: " <<  cur_vobj->type << std::endl;
372#endif
[173]373        if(cur_vobj->type == VOBJ_TYPE_BLOB)
[163]374        {
[173]375            size_t blob_size;
376            std::string filePath(m_pathHandler.getFullPath(std::string(cur_vobj->binpath)));
[163]377
378#ifdef MOVER_DEBUG
[173]379            std::cout << std::hex << "Handling: " << filePath << " ..." << std::endl;
[163]380#endif
[173]381
[163]382            if(!filePath.compare(m_path))    //local blob: map_info
383            {
384#ifdef MOVER_DEBUG
385                std::cout << "Found the vseg of the mapping info" << std::endl;
386#endif
[173]387                blob_size = this->m_size;
388                assert((blob_size >0) and "MAPPING INFO file is empty !?");
[163]389            }
390            else
[173]391            {
[163]392#ifdef MOVER_DEBUG
[173]393                std::cout << "Found an BLOB vseg" << std::endl;
[163]394#endif
[173]395                blob_size = bin_size(filePath);
396            }
[163]397
398
[173]399            /**creating a new section */
400            sect = new elfpp::section(*m_generator, elfpp::SHT_PROGBITS);
[163]401
[173]402            sect->set_name(std::string(cur_vobj->name));
403            sect->set_flags(elfpp::SHF_ALLOC | elfpp::SHF_WRITE);
404            sect->set_size(blob_size);//do the malloc for the get_content fonction
[163]405
[173]406            assert(sect->get_content());//check allocation
[163]407
[173]408            if(!filePath.compare(m_path))    //local blob: map_info
409                //memcpy(sect->get_content(), m_data, sect->get_size());
410                /* this way the modification of the elf size are propageted to the giet */
411                sect->set_content(this->m_data);
412            else
413                load_bin(filePath, sect->get_content());
[163]414
[173]415
416            if(blob_size > cur_vobj->length)
417            {
418                std::cout << std::hex << "!!! Warning, specified blob type vobj ("<<
419                cur_vobj->name  <<") size is "<< cur_vobj->length << ", the actual size is "
420                << blob_size  << " !!!" <<std::endl;
421                assert(0 and "blob vobj length smaller than the actual content" );//???
[163]422            }
423
[173]424            cur_vobj->length = blob_size;//set the true size of this BLOB vobj
425
426            vSO->m_file = filePath;
427            vSO->m_loadable = true;
428        }
429        else if(cur_vobj->type == VOBJ_TYPE_ELF)
430        {
431            if(!first)
432                throw exception::RunTimeError(std::string("elf vobj type, must be placed first in a vseg"));
433
434            size_t elf_size;
435            std::string filePath(m_pathHandler.getFullPath(std::string(cur_vobj->binpath)));
436#ifdef MOVER_DEBUG
437            std::cout << "Handling: " << filePath << " ..." << std::endl;
438            std::cout << "Found an ELF vseg" << std::endl;
439#endif
440            if(m_loaders.count(filePath) == 0 )
441                m_loaders[filePath] = new elfpp::object(filePath);
442            elfpp::object* loader = m_loaders[filePath];//TODO:free!?
443
[212]444            sect =  new elfpp::section(*get_sect_by_addr(loader, cur_vaddr));//copy: for the case we replicate the code
445            if (!sect)
446            {
447                std::cerr << "No section found for " << cur_vobj->name << " at "<< cur_vaddr << std::endl;
448                exit(-1);
449            }
[173]450
451            sect->set_name(std::string(cur_vobj->name));
452
453
454            elf_size = sect->get_size();
455            assert((elf_size > 0) and "ELF section empty ?");
456
457            if(!m_ginit)
458            {
459                /** Initailising the header of the generator from the first binary,
460                ** we suppose that the header is the same for all the binarys **/
461                m_generator->copy_info(*loader);
462                m_ginit=true;
463            }
464
[163]465            if(elf_size > cur_vobj->length)
[173]466            {
[163]467                std::cout << "Warning, specified elf type vobj ("<<
468                cur_vobj->name  <<") size is "<< cur_vobj->length << ", the actual size is "
469                << elf_size  << std::endl;
[173]470                //assert((elf_size < cur_vobj->length) and "elf vobj length smaller than the actual content" );//???
471                assert((0) and "elf vobj length smaller than the actual content" );//???
472            }
[163]473
474            cur_vobj->length = elf_size;//set the true size of this ELF vobj
475
476            vSO->m_file = filePath;
[173]477            vSO->m_loadable = true;
[163]478        }
479
480        //aligning the vobj->paddr if necessary
[173]481        //
[163]482        if(cur_vobj->align)
483        {
484            cur_paddr = PSeg::align(cur_paddr, cur_vobj->align);
485            aligned = true;
486        }
487
488        cur_vaddr += cur_vobj->length;
489        cur_paddr += cur_vobj->length;
490        simple_size += cur_vobj->length;
[173]491        first = false;
[163]492    }
493
494    assert((cur_vaddr >= vseg->vbase ));
495    assert((cur_paddr >= ps->nextLma() ));
496
497    vSO->m_length = (cur_paddr - ps->nextLma()); //pageAlign is done by the psegs
498
499#ifdef MOVER_DEBUG
500    if(aligned)
501    {
502        std::cout << "vseg base "<< std::hex << ps->nextLma()
503        <<(ps->nextLma()+simple_size)  <<" size " << std::dec << simple_size <<
[173]504        std::endl;
[163]505
506        std::cout << "vseg aligned to: base: " << std::hex << ps->nextLma()
507        <<" to "<< std::hex << ps->nextLma()+vSO->m_length<< " size " << std::dec <<
508        vSO->m_length << std::endl;
509    }
510#endif
511
[173]512    vSO->m_ident = vseg->ident;
513
[210]514    //should we check that we have the same type for the pseg and vobj?
[211]515    if(ps->type() != PSEG_TYPE_PERI)//take into acount only vseg who are not of the peri type
[210]516    {
517        //set the lma
518        if ( vseg->ident != 0 )            // identity mapping required
519            ps->addIdent( *vSO );
520        else
521            ps->add( *vSO );
522    }
[163]523    if(!sect)
524        return;
525
526#ifdef MOVER_DEBUG
527    std::cout << "section: "<< *sect <<"\n seted to: " << (*vSO) << std::endl;
528#endif
529
530    sect->set_vaddr((*vSO).lma());
[212]531    m_generator->add_section(*sect);
[163]532
533} // end vseg_map()
[173]534 
[163]535
[165]536///////////////////////////////
[163]537void MeMo::buildMap(void* desc)
538{
539    mapping_header_t*   header = (mapping_header_t*)desc; 
540
[187]541    mapping_cluster_t*  cluster = get_cluster_base( header );     
[163]542    mapping_vspace_t*   vspace = get_vspace_base( header );     
543    mapping_pseg_t*     pseg   = get_pseg_base( header ); 
544    mapping_vseg_t*     vseg   = get_vseg_base( header );
545
546    // get the psegs
[165]547
[163]548#ifdef MOVER_DEBUG
549std::cout << "\n******* Storing Pseg information *********\n" << std::endl;
550#endif
[165]551
[187]552#ifdef DISTRIBUTED_SCHEDULERS
553    char found;
554#endif
555
556    for ( size_t cluster_id = 0 ; cluster_id < header->clusters ; cluster_id++ )
[163]557    {
[187]558
559#ifdef DISTRIBUTED_SCHEDULERS
560        found    = 0;
561#endif
562
563        for ( size_t pseg_id = cluster[cluster_id].pseg_offset ;
564              pseg_id < cluster[cluster_id].pseg_offset + cluster[cluster_id].psegs ;
565              pseg_id++ )
566        {
567            //build pseg
568            std::string name(pseg[pseg_id].name);
[210]569            PSeg *ps = new PSeg(name, pseg[pseg_id].base, pseg[pseg_id].length, pseg[pseg_id].type);
[187]570
571#ifdef DISTRIBUTED_SCHEDULERS
572            if ( (pseg[pseg_id].type == PSEG_TYPE_RAM) && (found == 0) )
573            {
574                ps->incNextLma( (cluster[cluster_id].procs << 12) );
575                found  = 1;
576            }
[210]577            if(!found){/* we could imagine a cluster without proc, let the giet choose*/ }
[187]578#endif
579            m_psegh.m_pSegs.push_back(*ps);
580
581        }
[163]582    }
583
584    // map global vsegs
[165]585
[163]586#ifdef MOVER_DEBUG
587std::cout << "\n******* mapping global vsegs *********\n" << std::endl;
588#endif
[165]589
[163]590    for ( size_t vseg_id = 0 ; vseg_id < header->globals ; vseg_id++ )
591    {
592        vseg_map( &vseg[vseg_id]);
593    }
594
[165]595    // loop on virtual spaces to map private vsegs
[163]596    for (size_t vspace_id = 0 ; vspace_id < header->vspaces ; vspace_id++ )
597    {
598
599#ifdef MOVER_DEBUG
600std::cout << "\n******* mapping all vsegs of " << vspace[vspace_id].name << " *********\n" << std::endl;
601#endif
602           
603        for ( size_t vseg_id = vspace[vspace_id].vseg_offset ; 
604              vseg_id < (vspace[vspace_id].vseg_offset + vspace[vspace_id].vsegs) ; 
605              vseg_id++ )
606        {
607            vseg_map( &vseg[vseg_id]); 
608        }
609    } 
610
611} // end buildMap()
612
613
614
615// Local Variables:
616// tab-width: 4
617// c-basic-offset: 4
618// c-file-offsets:((innamespace . 0)(inline-open . 0))
619// indent-tabs-mode: nil
620// End:
621
622// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
623
Note: See TracBrowser for help on using the repository browser.