Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fc3f50ae9 | ||
|
|
d400e8eaf5 | ||
|
|
de2fac122c | ||
|
|
7707da0b45 |
@@ -1,10 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
make -j2
|
||||
|
||||
if [ "$TEST" != "NO" ]
|
||||
then
|
||||
make -j2 tapfiles
|
||||
make -j2 -s test-results
|
||||
fi
|
||||
@@ -1,83 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
cat << EOF > configure/RELEASE.local
|
||||
EPICS_BASE=$HOME/.source/epics-base
|
||||
EOF
|
||||
|
||||
install -d "$HOME/.source"
|
||||
cd "$HOME/.source"
|
||||
|
||||
git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
|
||||
(cd epics-base && git log -n1 )
|
||||
|
||||
EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
|
||||
|
||||
# requires wine and g++-mingw-w64-i686
|
||||
if [ "$WINE" = "32" ]
|
||||
then
|
||||
echo "Cross mingw32"
|
||||
sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
|
||||
CMPLR_PREFIX=i686-w64-mingw32-
|
||||
EOF
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ "$STATIC" = "YES" ]
|
||||
then
|
||||
echo "Build static libraries/executables"
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
SHARED_LIBRARIES=NO
|
||||
STATIC_BUILD=YES
|
||||
EOF
|
||||
fi
|
||||
|
||||
case "$CMPLR" in
|
||||
clang)
|
||||
echo "Host compiler is clang"
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
|
||||
GNU = NO
|
||||
CMPLR_CLASS = clang
|
||||
CC = clang
|
||||
CCC = clang++
|
||||
EOF
|
||||
|
||||
# hack
|
||||
sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
|
||||
|
||||
clang --version
|
||||
;;
|
||||
*)
|
||||
echo "Host compiler is default"
|
||||
gcc --version
|
||||
;;
|
||||
esac
|
||||
|
||||
cat <<EOF >> epics-base/configure/CONFIG_SITE
|
||||
USR_CPPFLAGS += $USR_CPPFLAGS
|
||||
USR_CFLAGS += $USR_CFLAGS
|
||||
USR_CXXFLAGS += $USR_CXXFLAGS
|
||||
EOF
|
||||
|
||||
# set RTEMS to eg. "4.9" or "4.10"
|
||||
# requires qemu, bison, flex, texinfo, install-info
|
||||
if [ -n "$RTEMS" ]
|
||||
then
|
||||
echo "Cross RTEMS${RTEMS} for pc386"
|
||||
curl -L "https://github.com/mdavidsaver/rsb/releases/download/20171203-${RTEMS}/i386-rtems${RTEMS}-trusty-20171203-${RTEMS}.tar.bz2" \
|
||||
| tar -C / -xmj
|
||||
|
||||
sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
|
||||
RTEMS_VERSION=$RTEMS
|
||||
RTEMS_BASE=$HOME/.rtems
|
||||
EOF
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu
|
||||
EOF
|
||||
fi
|
||||
|
||||
make -j2 -C epics-base $EXTRA
|
||||
30
.gitignore
vendored
30
.gitignore
vendored
@@ -1,17 +1,15 @@
|
||||
/cfg/
|
||||
/bin/
|
||||
/lib/
|
||||
/db/
|
||||
/dbd/
|
||||
/html/
|
||||
/include/
|
||||
/templates/
|
||||
/configure/*.local
|
||||
/configure/RELEASE.*
|
||||
/configure/CONFIG_SITE.*
|
||||
O.*/
|
||||
/QtC-*
|
||||
bin/
|
||||
lib/
|
||||
doc/
|
||||
include/
|
||||
db/
|
||||
dbd/
|
||||
documentation/html
|
||||
documentation/*.tag
|
||||
envPaths
|
||||
*.orig
|
||||
*.log
|
||||
.*.swp
|
||||
configure/*.local
|
||||
configure/RELEASE.*
|
||||
configure/CONFIG_SITE.*
|
||||
!configure/ExampleRELEASE.local
|
||||
**/O.*
|
||||
QtC-*
|
||||
|
||||
30
.travis.yml
30
.travis.yml
@@ -1,30 +0,0 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
language: c++
|
||||
compiler:
|
||||
- gcc
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libreadline6-dev
|
||||
- libncurses5-dev
|
||||
- perl
|
||||
- clang
|
||||
- g++-mingw-w64-i686
|
||||
- qemu-system-x86
|
||||
install:
|
||||
- ./.ci/travis-prepare.sh
|
||||
script:
|
||||
- ./.ci/travis-build.sh
|
||||
env:
|
||||
- BRBASE=7.0 CMPLR=gcc
|
||||
- BRBASE=7.0 CMPLR=clang
|
||||
- BRBASE=7.0 USR_CXXFLAGS=-std=c++11
|
||||
- BRBASE=7.0 CMPLR=clang USR_CXXFLAGS=-std=c++11
|
||||
- BRBASE=7.0 WINE=32 TEST=NO STATIC=YES
|
||||
- BRBASE=7.0 WINE=32 TEST=NO STATIC=NO
|
||||
- BRBASE=7.0 RTEMS=4.10 TEST=NO
|
||||
- BRBASE=7.0 RTEMS=4.9 TEST=NO
|
||||
- BRBASE=3.16
|
||||
- BRBASE=3.15
|
||||
- BRBASE=3.14
|
||||
31
COPYRIGHT
Normal file
31
COPYRIGHT
Normal file
@@ -0,0 +1,31 @@
|
||||
This software is in part copyrighted by the various organizations and
|
||||
individuals listed below. Permission to use it is set out in the file
|
||||
LICENSE that accompanies the software.
|
||||
|
||||
In no event shall any copyright holder be liable to any party for
|
||||
direct, indirect, special, incidental, or consequential damages arising
|
||||
out of the use of this software, its documentation, or any derivatives
|
||||
thereof, even if they have been advised of the possibility of such
|
||||
damage.
|
||||
|
||||
The copyright holders specifically disclaim any warranties, including,
|
||||
but not limited to, the implied warranties of merchantability, fitness
|
||||
for a particular purpose, and non-infringement. This software is
|
||||
provided on an "as is" basis, and the copyright holders have no
|
||||
obligation either collectively or individually to provide maintenance,
|
||||
support, updates, enhancements, or modifications.
|
||||
|
||||
Copyright (c) 2006 - 2015 All rights reserved
|
||||
|
||||
Martin R. Kraimer
|
||||
The University of Chicago, as Operator of Argonne National Laboratory.
|
||||
Deutsches Elektronen-Synchroton, Member of the Helmholtz Association,
|
||||
(DESY), HAMBURG, GERMANY,
|
||||
BERLINER SPEICHERRING GESELLSCHAFT FUER SYNCHROTRONSTRAHLUNG M.B.H.
|
||||
(BESSY), BERLIN, GERMANY.
|
||||
COSYLAB (Control System Laboratory), Ljubljana, Slovenia.
|
||||
Brookhaven Science Associates, as Operator of Brookhaven
|
||||
National Laboratory.
|
||||
Diamond Light Source Ltd., Didcot, United Kingdom.
|
||||
|
||||
|
||||
109
LICENSE
109
LICENSE
@@ -1,16 +1,14 @@
|
||||
Copyright and License Terms
|
||||
---------------------------
|
||||
|
||||
Copyright (c) 2006-2016 Martin R. Kraimer
|
||||
Copyright (c) 2006-2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
Copyright (c) 2006-2015 Martin R. Kraimer
|
||||
Copyright (c) 2006 The University of Chicago, as Operator of Argonne
|
||||
National Laboratory.
|
||||
Copyright (c) 2006 Deutsches Elektronen-Synchrotron,
|
||||
Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
|
||||
Copyright (c) 2007-2016 Control System Laboratory,
|
||||
Copyright (c) 2007-2015 Control System Laboratory,
|
||||
(COSYLAB) Ljubljana Slovenia
|
||||
Copyright (c) 2010-2016 Brookhaven Science Associates, as Operator
|
||||
of Brookhaven National Laboratory
|
||||
Copyright (c) 2011-2016 Diamond Light Source Limited,
|
||||
Copyright (c) 2010-2015 Brookhaven Science Associates, as Operator of Brookhaven
|
||||
National Laboratory
|
||||
Copyright (c) 2011-2015 Diamond Light Source Limited,
|
||||
(DLS) Didcot, United Kingdom
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
@@ -36,30 +34,81 @@ OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
________________________________________________________________________
|
||||
|
||||
Additional Disclaimers
|
||||
----------------------
|
||||
This software is in part copyrighted by the University of Chicago (UofC)
|
||||
|
||||
This software is copyright in part by these institutions:
|
||||
In no event shall UofC be liable to any party for direct, indirect,
|
||||
special, incidental, or consequential damages arising out of the use of
|
||||
this software, its documentation, or any derivatives thereof, even if
|
||||
UofC has been advised of the possibility of such damage.
|
||||
|
||||
* Brookhaven Science Associates, as Operator of Brookhaven
|
||||
National Laboratory, New York, USA
|
||||
* Control System Laboratory, Ljubljana, Slovenia
|
||||
* Deutsches Elektronen-Synchroton, Member of the Helmholtz
|
||||
Association, Hamburg, Germany
|
||||
* Diamond Light Source Limited, Didcot, United Kingdom
|
||||
* Helmholtz-Zentrum Berlin fuer Materialien und Energie m.b.H.,
|
||||
Berlin, Germany.
|
||||
* UChicage Argonne LLC, as Operator of Argonne National Laboratory,
|
||||
Illinois, USA
|
||||
UofC specifically disclaims any warranties, including, but not limited
|
||||
to, the implied warranties of merchantability, fitness for a particular
|
||||
purpose, and non-infringement. This software is provided on an "as is"
|
||||
basis, and UofC has no obligation to provide maintenance, support,
|
||||
updates, enhancements, or modifications.
|
||||
|
||||
In no event shall these institutions be liable to any party for direct,
|
||||
indirect, special, incidental, or consequential damages arising out of
|
||||
the use of this software, its documentation, or any derivatives thereof,
|
||||
even if advised of the possibility of such damage.
|
||||
________________________________________________________________________
|
||||
|
||||
These institutions specifically disclaim any warranties, including, but
|
||||
not limited to, the implied warranties of merchantability, fitness for a
|
||||
particular purpose, and non-infringement. This software is provided on
|
||||
an "as is" basis, and these institutions have no obligation to provide
|
||||
maintenance, support, updates, enhancements, or modifications.
|
||||
This software is in part copyrighted by the BERLINER SPEICHERRING
|
||||
GESELLSCHAFT FUER SYNCHROTRONSTRAHLUNG M.B.H. (BESSY), BERLIN, GERMANY.
|
||||
|
||||
In no event shall BESSY be liable to any party for direct, indirect,
|
||||
special, incidental, or consequential damages arising out of the use of
|
||||
this software, its documentation, or any derivatives thereof, even if
|
||||
BESSY has been advised of the possibility of such damage.
|
||||
|
||||
BESSY specifically disclaims any warranties, including, but not limited
|
||||
to, the implied warranties of merchantability, fitness for a particular
|
||||
purpose, and non-infringement. This software is provided on an "as is"
|
||||
basis, and BESSY has no obligation to provide maintenance, support,
|
||||
updates, enhancements, or modifications.
|
||||
|
||||
________________________________________________________________________
|
||||
|
||||
This software is in part copyrighted by the Deutsches Elektronen-Synchroton,
|
||||
Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
|
||||
|
||||
In no event shall DESY be liable to any party for direct, indirect,
|
||||
special, incidental, or consequential damages arising out of the use of
|
||||
this software, its documentation, or any derivatives thereof, even if
|
||||
DESY has been advised of the possibility of such damage.
|
||||
|
||||
DESY specifically disclaims any warranties, including, but not limited
|
||||
to, the implied warranties of merchantability, fitness for a particular
|
||||
purpose, and non-infringement. This software is provided on an "as is"
|
||||
basis, and DESY has no obligation to provide maintenance, support,
|
||||
updates, enhancements, or modifications.
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
This software is in part copyrighted by the Brookhaven
|
||||
National Laboratory (BNL).
|
||||
|
||||
In no event shall BNL be liable to any party for direct, indirect,
|
||||
special, incidental, or consequential damages arising out of the use of
|
||||
this software, its documentation, or any derivatives thereof, even if
|
||||
BNL has been advised of the possibility of such damage.
|
||||
|
||||
BNL specifically disclaims any warranties, including, but not limited
|
||||
to, the implied warranties of merchantability, fitness for a particular
|
||||
purpose, and non-infringement. This software is provided on an "as is"
|
||||
basis, and BNL has no obligation to provide maintenance, support,
|
||||
updates, enhancements, or modifications.
|
||||
|
||||
________________________________________________________________________
|
||||
|
||||
This software is in part copyrighted by Diamond Light Source Limited (DLS)
|
||||
|
||||
In no event shall DLS be liable to any party for direct, indirect,
|
||||
special, incidental, or consequential damages arising out of the use of
|
||||
this software, its documentation, or any derivatives thereof, even if
|
||||
DLS has been advised of the possibility of such damage.
|
||||
|
||||
DLS specifically disclaims any warranties, including, but not limited
|
||||
to, the implied warranties of merchantability, fitness for a particular
|
||||
purpose, and non-infringement. This software is provided on an "as is"
|
||||
basis, and DLS has no obligation to provide maintenance, support,
|
||||
updates, enhancements, or modifications.
|
||||
|
||||
________________________________________________________________________
|
||||
|
||||
|
||||
3
Makefile
3
Makefile
@@ -11,7 +11,4 @@ src_DEPEND_DIRS = configure
|
||||
DIRS += testApp
|
||||
testApp_DEPEND_DIRS = src
|
||||
|
||||
DIRS += examples
|
||||
examples_DEPEND_DIRS = src
|
||||
|
||||
include $(TOP)/configure/RULES_TOP
|
||||
|
||||
61
README.md
61
README.md
@@ -1,16 +1,57 @@
|
||||
# pvaDataCPP
|
||||
pvaDataCPP
|
||||
==========
|
||||
|
||||
The EPICS **pvData** API provides a set of classes and utilities that form the core of the EPICS PVA implementation.
|
||||
pvDataCPP is a set of data types and utilities that form part of the EPICS V4 project.
|
||||
|
||||
The pvDataCPP module is a part of the EPICS software toolkit that implements pvData structures as C++ class objects.
|
||||
|
||||
## Links
|
||||
Further Info
|
||||
------------
|
||||
|
||||
- General information about EPICS can be found at the
|
||||
[EPICS Controls website](https://epics-controls.org).
|
||||
- API documentation for this module can be found on its
|
||||
[Github Pages website](https://epics-base.github.io/pvDataCPP/).
|
||||
Consult the documents in the documentation directory, in particular
|
||||
|
||||
* pvDataCPP.html
|
||||
* RELEASE_NOTES.md
|
||||
|
||||
Also see the [EPICS Version 4 website](http://epics-pvdata.sourceforge.net)
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
The pvDataCPP requires recent versions of the following software:
|
||||
|
||||
1. EPICS Base (v3.14.12.3 or later)
|
||||
2. EPICS4 pvCommonCPP (4.1.0 or later)
|
||||
|
||||
(pvCommonCPP may not be needed depending on host/compiler.)
|
||||
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
Building uses the make utility and the EPICS base build system.
|
||||
|
||||
The build system needs the location of the prerequisites, e.g. by placing the
|
||||
lines of the form
|
||||
|
||||
PVCOMMON = /home/install/epicsV4/pvCommonCPP
|
||||
EPICS_BASE = /home/install/epics/base
|
||||
|
||||
pointing to the locations in a file called RELEASE.local
|
||||
in the configure directory or the parent directory of pvDataCPP.
|
||||
|
||||
With this in place, to build type make
|
||||
|
||||
make
|
||||
|
||||
To perform a clean build type
|
||||
|
||||
make clean uninstall
|
||||
|
||||
To run the unit tests type
|
||||
|
||||
make runtests
|
||||
|
||||
For more information on the EPICS build system consult the
|
||||
[Application Development guide](http://www.aps.anl.gov/epics/base/R3-14/12-docs/AppDevGuide.pdf).
|
||||
|
||||
## Building
|
||||
|
||||
This module is included as a submodule of a full EPICS 7 release and will be compiled during builds of that software.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
EPICS_PVD_MAJOR_VERSION = 7
|
||||
EPICS_PVD_MINOR_VERSION = 1
|
||||
EPICS_PVD_MAINTENANCE_VERSION = 3
|
||||
EPICS_PVD_DEVELOPMENT_FLAG = 0
|
||||
@@ -20,14 +20,9 @@ CHECK_RELEASE = YES
|
||||
# INSTALL_LOCATION here.
|
||||
#INSTALL_LOCATION=</path/name/to/install/top>
|
||||
|
||||
USR_CPPFLAGS += -DPVD_INTERNAL
|
||||
|
||||
-include $(TOP)/../CONFIG_SITE.local
|
||||
-include $(TOP)/configure/CONFIG_SITE.local
|
||||
|
||||
# MSVC - skip defining min()/max() macros
|
||||
USR_CPPFLAGS_WIN32 += -DNOMINMAX
|
||||
|
||||
ifdef WITH_COVERAGE
|
||||
USR_CPPFLAGS += --coverage
|
||||
USR_LDFLAGS += --coverage
|
||||
|
||||
@@ -2,8 +2,6 @@ TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
CFG += CONFIG_PVDATA_VERSION
|
||||
|
||||
TARGETS = $(CONFIG_TARGETS)
|
||||
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
|
||||
|
||||
|
||||
@@ -1,38 +1,25 @@
|
||||
# RELEASE - Location of external support modules
|
||||
# pvDataCPP RELEASE - Location of external support modules
|
||||
#
|
||||
# IF YOU CHANGE ANY PATHS in this file or make API changes to
|
||||
# any modules it refers to, you should do a "make rebuild" in
|
||||
# this application's top level directory.
|
||||
# IF YOU CHANGE this file or any file it includes you must
|
||||
# subsequently do a "gnumake rebuild" in the application's
|
||||
# top level directory.
|
||||
#
|
||||
# The EPICS build process does not check dependencies against
|
||||
# any files from outside the application, so it is safest to
|
||||
# rebuild it completely if any modules it depends on change.
|
||||
# The build process does not check dependencies against files
|
||||
# that are outside this application, thus you should also do a
|
||||
# "gnumake rebuild" in the top level directory after EPICS_BASE
|
||||
# or any other external module pointed to below is rebuilt.
|
||||
#
|
||||
# Host- or target-specific settings can be given in files named
|
||||
# RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
# RELEASE.Common.$(T_A)
|
||||
# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
|
||||
# EPICS V4 Developers: Do not edit the locations in this file!
|
||||
#
|
||||
# This file is parsed by both GNUmake and an EPICS Perl script,
|
||||
# so it may ONLY contain definititions of paths to other support
|
||||
# modules, variable definitions that are used in module paths,
|
||||
# and include statements that pull in other RELEASE files.
|
||||
# Variables may be used before their values have been set.
|
||||
# Build variables that are NOT used in paths should be set in
|
||||
# the CONFIG_SITE file.
|
||||
# Create a file RELEASE.local pointing to your PVCOMMON
|
||||
# and EPICS_BASE build directories, e.g.
|
||||
# PVCOMMON = /home/install/epicsV4/pvCommonCPP
|
||||
# EPICS_BASE = /home/install/epics/base
|
||||
|
||||
# Variables and paths to dependent modules:
|
||||
#MODULES = /path/to/modules
|
||||
#MYMODULE = $(MODULES)/my-module
|
||||
|
||||
# If building the EPICS modules individually, set these:
|
||||
#EPICS_BASE = /path/to/base
|
||||
|
||||
# Set RULES here if you want to use build rules from elsewhere:
|
||||
#RULES = $(MODULES)/build-rules
|
||||
|
||||
# These allow developers to override the RELEASE variable settings
|
||||
# without having to modify the configure/RELEASE file itself.
|
||||
-include $(TOP)/../RELEASE.local
|
||||
-include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
|
||||
-include $(TOP)/configure/RELEASE.local
|
||||
|
||||
3
documentation/.gitignore
vendored
3
documentation/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
*.tag
|
||||
*.db
|
||||
html/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
all: gen
|
||||
|
||||
clean:
|
||||
rm -rf doxygen_sqlite3.db html
|
||||
|
||||
gen: libstdc++.tag
|
||||
doxygen
|
||||
|
||||
commit: gen
|
||||
touch html/.nojekyll
|
||||
./commit-gh.sh documentation/html/ html/.nojekyll html/*.* html/search/*.*
|
||||
|
||||
libstdc++.tag:
|
||||
wget -O $@ https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/libstdc++.tag
|
||||
|
||||
.PHONY: all clean gen commit
|
||||
218
documentation/RELEASE_NOTES.html
Normal file
218
documentation/RELEASE_NOTES.html
Normal file
@@ -0,0 +1,218 @@
|
||||
<h1>Release 5.0</h1>
|
||||
|
||||
<p>The main changes since release 4.0 are:</p>
|
||||
|
||||
<ul>
|
||||
<li>Deprecated getXXXField() methods have been removed from PVStructure</li>
|
||||
<li>Convert copy methods and equals operators (re)moved</li>
|
||||
<li>Convert::copyUnion now always copies between subfields.</li>
|
||||
<li>New method getSubFieldT, like getSubField except it throws an exception</li>
|
||||
<li>findSubField method removed from PVStructure</li>
|
||||
<li>New stream operators for Field and PVField are provided</li>
|
||||
<li>New template versions of Structure::getField</li>
|
||||
<li>Fixes for static initialisation order issues</li>
|
||||
<li>CreateRequest prevents a possible SEGFAULT</li>
|
||||
</ul>
|
||||
|
||||
<h2>Deprecated getXXXField methods have been removed from PVStructure</h2>
|
||||
|
||||
<p>The following methods have been removed from PVStructure</p>
|
||||
|
||||
<ul>
|
||||
<li>getBooleanField</li>
|
||||
<li>getByteField, getShortField, getIntField, getLongField</li>
|
||||
<li>getUByteField, getUShortField, getUIntField, getULongField</li>
|
||||
<li>getStringField</li>
|
||||
<li>getStructureField, getUnionField</li>
|
||||
<li>getScalarArrayField, getStructureArrayField, getUnionArrayField</li>
|
||||
</ul>
|
||||
|
||||
<p>Use template getSubField instead, e.g. use</p>
|
||||
|
||||
<pre><code>getSubField< PVInt >(fieldName)
|
||||
</code></pre>
|
||||
|
||||
<p>in place of</p>
|
||||
|
||||
<pre><code>getIntField(fieldName)
|
||||
</code></pre>
|
||||
|
||||
<h2>Convert copy methods and equals operators</h2>
|
||||
|
||||
<p>Convert copy methods where moved and replaced with methods
|
||||
on PVField classes, i.e.</p>
|
||||
|
||||
<pre><code>PVField::copy(const PVField& from)
|
||||
</code></pre>
|
||||
|
||||
<p>Methods</p>
|
||||
|
||||
<pre><code>PVField::copyUnchecked(const PVField& from)
|
||||
</code></pre>
|
||||
|
||||
<p>were added to allow unchecked copies, to gain performance
|
||||
where checked are not needed (anymore).</p>
|
||||
|
||||
<p>In addition:
|
||||
- isCompatibleXXX methods were removed in favour of Field::operator==.
|
||||
- equals methods were remove in favour of PVField::operator==.
|
||||
- operator== methods where moved to pvIntrospect.h and pvData.h</p>
|
||||
|
||||
<h2>Convert::copyUnion</h2>
|
||||
|
||||
<p>Before this method, depending on types for to and from,
|
||||
sometimes did a shallow copy, i.e. just made to shared_ptr for to
|
||||
share the same data as from.
|
||||
Now it always copies between the subfield of to and from.</p>
|
||||
|
||||
<h2>New method getSubFieldT, like getSubField except it throws an exception</h2>
|
||||
|
||||
<p>PVStructure has a new template member</p>
|
||||
|
||||
<pre><code>getSubFieldT(std::string const &fieldName)
|
||||
</code></pre>
|
||||
|
||||
<p>that is like <b>getSubField</b> except that it throws a runtime_error
|
||||
instead of returning null.</p>
|
||||
|
||||
<h2>findSubField method removed from PVStructure</h2>
|
||||
|
||||
<p>This was mainly used in the implementation of getSubField. With a change to
|
||||
the latter, findSubField was removed.</p>
|
||||
|
||||
<h2>New stream operators</h2>
|
||||
|
||||
<p>New steam operators are available for Field and PVField.
|
||||
Before to print a Field (or any extension) or a PVField (or any extension)
|
||||
it was necessary to have code like:</p>
|
||||
|
||||
<pre><code> void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
if(struc) {
|
||||
cout << *struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
if(pv) {
|
||||
cout << *.struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<p>Now it can be done as follows:</p>
|
||||
|
||||
<pre><code> void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
cout << struc << endl;
|
||||
cout << pv << endl;
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<h2>New template version of Structure::getField</h2>
|
||||
|
||||
<p>A new template getField method has been added to Structure</p>
|
||||
|
||||
<p>template<typename FT >
|
||||
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const </p>
|
||||
|
||||
<p>Can be used, for example, as follows:</p>
|
||||
|
||||
<pre><code>StructurePtr tsStruc = struc->getField<Structure>("timeStamp");
|
||||
</code></pre>
|
||||
|
||||
<h2>Fixes for static initialisation order issues</h2>
|
||||
|
||||
<p>Certain static builds (in particular Windows builds) of applications using
|
||||
pvData had issues due to PVStructure::DEFAULT_ID being used before being initialised. This has been fixed.</p>
|
||||
|
||||
<h2>CreateRequest change</h2>
|
||||
|
||||
<p>createRequest could cause a SEGFAULT if passed a bad argument.
|
||||
This has been changed so the it returns a null pvStructure
|
||||
and provides an error.</p>
|
||||
|
||||
<h1>Release 4.0</h1>
|
||||
|
||||
<p>The main changes since release 3.0.2 are:</p>
|
||||
|
||||
<ul>
|
||||
<li>array semantics now enforce Copy On Write.</li>
|
||||
<li>String no longer defined.</li>
|
||||
<li>timeStamp and valueAlarm name changes</li>
|
||||
<li>toString replaced by stream I/O </li>
|
||||
<li>union is new type.</li>
|
||||
<li>copy is new.</li>
|
||||
<li>monitorPlugin is new.</li>
|
||||
</ul>
|
||||
|
||||
<h2>New Semantics for Arrays</h2>
|
||||
|
||||
<p>PVScalarArray, PVStructureArray, and PVUnionArray all enforce COW (Copy On Write) Semantics.
|
||||
In order to limit memory usage the storage for raw data is managed via a new shared<em>vector facility.
|
||||
This allows multiple instances of array data to use the shared raw data.
|
||||
COW is implemented via shared</em>vectors of const data, i. e. data that can not be modified.</p>
|
||||
|
||||
<h2>String no longer defined</h2>
|
||||
|
||||
<p>This is replaced by std::string.</p>
|
||||
|
||||
<h2>timeStamp and valueAlarm name changes</h2>
|
||||
|
||||
<p>In timeStamp nanoSeconds is changed to nanoseconds.</p>
|
||||
|
||||
<p>In valueAlarm hysteresis is changed to hysteresis</p>
|
||||
|
||||
<h2>toString replaced by stream I/O</h2>
|
||||
|
||||
<p>pvData.h and pvIntrospect no longer defines toString
|
||||
Instead they have stream support.
|
||||
pvIntrospect uses method dump and pvData uses dumpValue.
|
||||
For example:</p>
|
||||
|
||||
<pre><code> PVDoublePtr pvValue;
|
||||
String buffer;
|
||||
pvValue->toString(&buffer);
|
||||
cout << buffer << endl;
|
||||
buffer.clear();
|
||||
pvValue->getField()->toString(&buffer);
|
||||
cout << buffer << evdl;
|
||||
</code></pre>
|
||||
|
||||
<p>is replaced by</p>
|
||||
|
||||
<pre><code> PVDoublePtr pvValue;
|
||||
cout << *pvValue << endl
|
||||
cout << *pvValue->getField() << endl;
|
||||
</code></pre>
|
||||
|
||||
<h2>union is a new basic type.</h2>
|
||||
|
||||
<p>There are two new basic types: union_t and unionArray.</p>
|
||||
|
||||
<p>A union is like a structure that has a single subfield.
|
||||
There are two flavors:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>variant union</b> The field can have any type.</li>
|
||||
<li><b>union</b> The field can any of specified set of types.</li>
|
||||
</ul>
|
||||
|
||||
<p>The field type can be dynamically changed.</p>
|
||||
|
||||
<h2>copy </h2>
|
||||
|
||||
<p>This consists of createRequest and pvCopy.
|
||||
createRequest was moved from pvAccess to here.
|
||||
pvCopy is moved from pvDatabaseCPP and now depends
|
||||
only on pvData, i.e. it no longer has any knowledge of PVRecord.</p>
|
||||
|
||||
<h2>monitorPlugin</h2>
|
||||
|
||||
<p>This is for is for use by code that implements pvAccess monitors.
|
||||
This is prototype and is subject to debate.</p>
|
||||
|
||||
<h1>Release 3.0.2</h1>
|
||||
|
||||
<p>This was the starting point for RELEASE_NOTES</p>
|
||||
232
documentation/RELEASE_NOTES.md
Normal file
232
documentation/RELEASE_NOTES.md
Normal file
@@ -0,0 +1,232 @@
|
||||
Release 5.0
|
||||
===========
|
||||
|
||||
The main changes since release 4.0 are:
|
||||
|
||||
* Deprecated getXXXField() methods have been removed from PVStructure
|
||||
* Convert copy methods and equals operators (re)moved
|
||||
* Convert::copyUnion now always copies between subfields.
|
||||
* New method getSubFieldT, like getSubField except it throws an exception
|
||||
* findSubField method removed from PVStructure
|
||||
* New stream operators for Field and PVField are provided
|
||||
* New template versions of Structure::getField
|
||||
* Fixes for static initialisation order issues
|
||||
* CreateRequest prevents a possible SEGFAULT
|
||||
|
||||
|
||||
Deprecated getXXXField methods have been removed from PVStructure
|
||||
-------------------------------------------------------------------
|
||||
|
||||
The following methods have been removed from PVStructure
|
||||
|
||||
* getBooleanField
|
||||
* getByteField, getShortField, getIntField, getLongField
|
||||
* getUByteField, getUShortField, getUIntField, getULongField
|
||||
* getStringField
|
||||
* getStructureField, getUnionField
|
||||
* getScalarArrayField, getStructureArrayField, getUnionArrayField
|
||||
|
||||
Use template getSubField instead, e.g. use
|
||||
|
||||
getSubField< PVInt >(fieldName)
|
||||
|
||||
in place of
|
||||
|
||||
getIntField(fieldName)
|
||||
|
||||
|
||||
Convert copy methods and equals operators
|
||||
-----------------------------------------
|
||||
|
||||
Convert copy methods where moved and replaced with methods
|
||||
on PVField classes, i.e.
|
||||
|
||||
PVField::copy(const PVField& from)
|
||||
|
||||
Methods
|
||||
|
||||
PVField::copyUnchecked(const PVField& from)
|
||||
|
||||
were added to allow unchecked copies, to gain performance
|
||||
where checked are not needed (anymore).
|
||||
|
||||
In addition:
|
||||
- isCompatibleXXX methods were removed in favour of Field::operator==.
|
||||
- equals methods were remove in favour of PVField::operator==.
|
||||
- operator== methods where moved to pvIntrospect.h and pvData.h
|
||||
|
||||
|
||||
Convert::copyUnion
|
||||
-----------------
|
||||
|
||||
Before this method, depending on types for to and from,
|
||||
sometimes did a shallow copy, i.e. just made to shared_ptr for to
|
||||
share the same data as from.
|
||||
Now it always copies between the subfield of to and from.
|
||||
|
||||
|
||||
New method getSubFieldT, like getSubField except it throws an exception
|
||||
--------------------
|
||||
|
||||
PVStructure has a new template member
|
||||
|
||||
getSubFieldT(std::string const &fieldName)
|
||||
|
||||
that is like <b>getSubField</b> except that it throws a runtime_error
|
||||
instead of returning null.
|
||||
|
||||
|
||||
findSubField method removed from PVStructure
|
||||
--------------------------------------------
|
||||
|
||||
This was mainly used in the implementation of getSubField. With a change to
|
||||
the latter, findSubField was removed.
|
||||
|
||||
|
||||
New stream operators
|
||||
--------------------
|
||||
|
||||
New steam operators are available for Field and PVField.
|
||||
Before to print a Field (or any extension) or a PVField (or any extension)
|
||||
it was necessary to have code like:
|
||||
|
||||
void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
if(struc) {
|
||||
cout << *struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
if(pv) {
|
||||
cout << *.struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
}
|
||||
|
||||
Now it can be done as follows:
|
||||
|
||||
void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
cout << struc << endl;
|
||||
cout << pv << endl;
|
||||
}
|
||||
|
||||
|
||||
New template version of Structure::getField
|
||||
--------------------------------------------
|
||||
|
||||
A new template getField method has been added to Structure
|
||||
|
||||
template<typename FT >
|
||||
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
|
||||
|
||||
Can be used, for example, as follows:
|
||||
|
||||
StructurePtr tsStruc = struc->getField<Structure>("timeStamp");
|
||||
|
||||
|
||||
Fixes for static initialisation order issues
|
||||
--------------------------------------------
|
||||
|
||||
Certain static builds (in particular Windows builds) of applications using
|
||||
pvData had issues due to PVStructure::DEFAULT_ID being used before being initialised. This has been fixed.
|
||||
|
||||
|
||||
CreateRequest change
|
||||
--------------------
|
||||
|
||||
createRequest could cause a SEGFAULT if passed a bad argument.
|
||||
This has been changed so the it returns a null pvStructure
|
||||
and provides an error.
|
||||
|
||||
|
||||
Release 4.0
|
||||
===========
|
||||
|
||||
The main changes since release 3.0.2 are:
|
||||
|
||||
* array semantics now enforce Copy On Write.
|
||||
* String no longer defined.
|
||||
* timeStamp and valueAlarm name changes
|
||||
* toString replaced by stream I/O
|
||||
* union is new type.
|
||||
* copy is new.
|
||||
* monitorPlugin is new.
|
||||
|
||||
New Semantics for Arrays
|
||||
--------
|
||||
|
||||
PVScalarArray, PVStructureArray, and PVUnionArray all enforce COW (Copy On Write) Semantics.
|
||||
In order to limit memory usage the storage for raw data is managed via a new shared_vector facility.
|
||||
This allows multiple instances of array data to use the shared raw data.
|
||||
COW is implemented via shared_vectors of const data, i. e. data that can not be modified.
|
||||
|
||||
|
||||
String no longer defined
|
||||
---------
|
||||
|
||||
This is replaced by std::string.
|
||||
|
||||
|
||||
timeStamp and valueAlarm name changes
|
||||
--------------
|
||||
|
||||
In timeStamp nanoSeconds is changed to nanoseconds.
|
||||
|
||||
In valueAlarm hysteresis is changed to hysteresis
|
||||
|
||||
|
||||
toString replaced by stream I/O
|
||||
---------
|
||||
|
||||
pvData.h and pvIntrospect no longer defines toString
|
||||
Instead they have stream support.
|
||||
pvIntrospect uses method dump and pvData uses dumpValue.
|
||||
For example:
|
||||
|
||||
PVDoublePtr pvValue;
|
||||
String buffer;
|
||||
pvValue->toString(&buffer);
|
||||
cout << buffer << endl;
|
||||
buffer.clear();
|
||||
pvValue->getField()->toString(&buffer);
|
||||
cout << buffer << evdl;
|
||||
|
||||
is replaced by
|
||||
|
||||
PVDoublePtr pvValue;
|
||||
cout << *pvValue << endl
|
||||
cout << *pvValue->getField() << endl;
|
||||
|
||||
|
||||
union is a new basic type.
|
||||
------------
|
||||
|
||||
There are two new basic types: union_t and unionArray.
|
||||
|
||||
A union is like a structure that has a single subfield.
|
||||
There are two flavors:
|
||||
|
||||
* <b>variant union</b> The field can have any type.
|
||||
* <b>union</b> The field can any of specified set of types.
|
||||
|
||||
The field type can be dynamically changed.
|
||||
|
||||
copy
|
||||
----
|
||||
|
||||
This consists of createRequest and pvCopy.
|
||||
createRequest was moved from pvAccess to here.
|
||||
pvCopy is moved from pvDatabaseCPP and now depends
|
||||
only on pvData, i.e. it no longer has any knowledge of PVRecord.
|
||||
|
||||
monitorPlugin
|
||||
-------------
|
||||
|
||||
This is for is for use by code that implements pvAccess monitors.
|
||||
This is prototype and is subject to debate.
|
||||
|
||||
Release 3.0.2
|
||||
==========
|
||||
This was the starting point for RELEASE_NOTES
|
||||
@@ -12,3 +12,9 @@ valueAlarm
|
||||
normativeTypes.html describes valueAlarm only for a value field that has type
|
||||
double.
|
||||
The implementation also supports all the numeric scalar types.
|
||||
|
||||
monitorPlugin
|
||||
-------------
|
||||
|
||||
A debate is on-going about what semantics should be.
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
# Usage: commit-gh <sub-directory-prefix> <files...>
|
||||
#
|
||||
# Creates a commit containing only the files in the sub-directory provided as an argument
|
||||
#
|
||||
# Does not disturb the working copy or index
|
||||
|
||||
prefix="$1"
|
||||
shift
|
||||
|
||||
# Commit to this branch
|
||||
BRANCH=refs/heads/gh-pages
|
||||
|
||||
# Use the main branch description as the gh-pages commit message
|
||||
MSG=`git describe --tags --always`
|
||||
|
||||
# Scratch space
|
||||
TDIR=`mktemp -d -p $PWD`
|
||||
|
||||
# Automatic cleanup of scratch space
|
||||
trap 'rm -rf $TDIR' INT TERM QUIT EXIT
|
||||
|
||||
export GIT_INDEX_FILE="$TDIR/index"
|
||||
|
||||
# Add listed files to a new (empty) index
|
||||
git update-index --add "$@"
|
||||
|
||||
# Write the index into the repo, get tree hash
|
||||
TREE=`git write-tree --prefix="$prefix"`
|
||||
|
||||
echo "TREE $TREE"
|
||||
git cat-file -p $TREE
|
||||
|
||||
# Create a commit with our new tree
|
||||
# Reference current branch head as parent (if any)
|
||||
CMT=`git commit-tree -m "$MSG" $TREE`
|
||||
|
||||
echo "COMMIT $CMT"
|
||||
git cat-file -p $CMT
|
||||
|
||||
# Update the branch with the new commit tree hash
|
||||
git update-ref $BRANCH $CMT
|
||||
|
||||
echo "Done"
|
||||
679
documentation/copyandmonitor.html
Normal file
679
documentation/copyandmonitor.html
Normal file
@@ -0,0 +1,679 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
|
||||
<title>EPICS pvDataCPP: copy and monitor</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/base.css" />
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
|
||||
<style type="text/css">
|
||||
/*<![CDATA[*/
|
||||
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
|
||||
table { margin-left: auto; margin-right: auto }
|
||||
.diagram { text-align: center; margin: 2.5em 0 }
|
||||
span.opt { color: grey }
|
||||
span.nterm { font-style:italic }
|
||||
span.term { font-family:courier }
|
||||
span.user { font-family:courier }
|
||||
span.user:before { content:"<" }
|
||||
span.user:after { content:">" }
|
||||
.nonnorm { font-style:italic }
|
||||
p.ed { color: #AA0000 }
|
||||
span.ed { color: #AA0000 }
|
||||
p.ed.priv { display: inline; }
|
||||
span.ed.priv { display: inline; }
|
||||
/*]]>*/</style>
|
||||
<!-- Script that generates the Table of Contents -->
|
||||
<script type="text/javascript"
|
||||
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div id="toc">
|
||||
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
|
||||
</div>
|
||||
<div id="contents" class="contents">
|
||||
|
||||
<h2>support for copy and monitor</h2>
|
||||
<p><b>copy</b> and <b>monitor</b> are not used in this project.
|
||||
They are intended for use by pvAccess and by pvAccess servers.
|
||||
They are provided with this project because the code depends only on
|
||||
pvData itself.
|
||||
</p>
|
||||
<p>This document describes C++ specific code.
|
||||
<a href="http://epics-pvdata.sourceforge.net/informative/pvRequest.html">
|
||||
pvRequest.html</a>
|
||||
provides a language independent overview of <b>copy</b> and <b>monitor</b>.
|
||||
</p>
|
||||
<p>
|
||||
<b>NOTE:pvRequest.html</b> must be updated since it is based on an earlier version of pvCopy that
|
||||
had knowledge of PVRecord. The C++ version was implemented in pvDatabaseCPP
|
||||
and the Java version on pvIOCJava.
|
||||
At present only the C++ version of the new API for pvCopy is implemented.
|
||||
</p>
|
||||
<p>Copy provides:
|
||||
<dl>
|
||||
<dt>createRequest</dt>
|
||||
<dd>
|
||||
The Channel create methods in pvAccess all have an argument
|
||||
<b>PVStructure pvRequest</b>.<br />
|
||||
Given an ascii string createRequest creates a PVStructure that provides
|
||||
a pvData representation of the information from the ascii string.
|
||||
It is this structure that can be passed to the channel create methods.<br />
|
||||
The information in a pvRequest selects an arbitrary subset of the
|
||||
fields in a top level structure that resides in the server.
|
||||
In addition options can be specified. Both global and field specific
|
||||
options can be specified.
|
||||
</dd>
|
||||
<dt>pvCopy</dt>
|
||||
<dd>This is a facility used by channel providers.
|
||||
It provides client specific code that manages a copy of an arbitrary
|
||||
subset of the fields in a top level structure that resides in the
|
||||
provider. It also allows provider access to options specified
|
||||
by the client.
|
||||
</dd>
|
||||
</dl>
|
||||
Monitor provides:
|
||||
<dl>
|
||||
<dt>monitor</dt>
|
||||
<dd>This is support code for channel providers that implement channel
|
||||
monitor. It, together with the queue facility, provides support for
|
||||
monitor queues.
|
||||
</dd>
|
||||
<dt>monitorPlugin</dt>
|
||||
<dd>This is support for implementing monitor plugins.
|
||||
A monitor plugin can be developed that has no knowledge
|
||||
of pvAccess but only pvData.
|
||||
</dd>
|
||||
</dl>
|
||||
</p>
|
||||
|
||||
<h2>support for copy</h2>
|
||||
<p><b>copy</b> provides the ability to create a structure that has
|
||||
a copy of an arbitrary subset of the fields in an existing top level
|
||||
structure. In addition it allows global options and field specific options.
|
||||
It has two main components: <b>createRequest</b> and <b>pvCopy</b>.
|
||||
Given a string createRequest creates a pvRequest, which is a PVStructure
|
||||
that has the format expected by <b>pvCopy</b>.
|
||||
</p>
|
||||
|
||||
<h3>createRequest</h3>
|
||||
<p>This is mainly used by pvAccess clients. Given a request string it creates
|
||||
a pvRequest structure that can be passed to the pvAccess create methods.
|
||||
In turn pvAccess passes the pvRequest to a local channel provider which
|
||||
then passes it to pvCopy.
|
||||
</p>
|
||||
<p>The definition of the public members is:</p>
|
||||
<pre>
|
||||
class CreateRequest {
|
||||
...
|
||||
static CreateRequestPtr create();
|
||||
virtual PVStructurePtr createRequest(std::string const &request);
|
||||
std::string getMessage();
|
||||
};
|
||||
</pre>
|
||||
<p>An example of how it is used is:</p>
|
||||
<pre>
|
||||
CreateRequestPtr createRequest = CreateRequest::create();
|
||||
PVStructurePtr pvRequest = createRequest->createRequest(request);
|
||||
if(pvRequest==NULL) {
|
||||
std::string error = createRequest->getMessage();
|
||||
// take some action
|
||||
} else {
|
||||
//success do something
|
||||
}
|
||||
</pre>
|
||||
<h3>pvCopy</h3>
|
||||
<p>The definition of the public members is:</p>
|
||||
<pre>
|
||||
class epicsShareClass PVCopyTraverseMasterCallback
|
||||
{
|
||||
...
|
||||
virtual void nextMasterPVField(PVFieldPtr const &pvField);
|
||||
};
|
||||
|
||||
class class epicsShareClass PVCopy
|
||||
{
|
||||
...
|
||||
static PVCopyPtr create(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvRequest,
|
||||
std::string const & structureName);
|
||||
PVStructurePtr getPVMaster();
|
||||
void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback);
|
||||
StructureConstPtr getStructure();
|
||||
PVStructurePtr createPVStructure();
|
||||
size_t getCopyOffset(PVFieldPtr const &masterPVField);
|
||||
size_t getCopyOffset(
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField);
|
||||
PVFieldPtr getMasterPVField(std::size_t structureOffset);
|
||||
void initCopy(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateCopySetBitSet(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateCopyFromBitSet(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateMaster(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
PVStructurePtr getOptions(std::size_t fieldOffset);
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
where
|
||||
<dl>
|
||||
<dt>PVCopyTraverseMasterCallback::nextMasterPVField</dt>
|
||||
<dd>
|
||||
<b>PVCopyTraverseMasterCallback</b> is a callback which must
|
||||
be implemented by the code that uses pvCopy, normally
|
||||
the channel provider. It has the single method <b>nextMasterPVField</b>
|
||||
<br />
|
||||
<b>nextMasterPVField</b> is called for each field in the master
|
||||
as a result of a call to <b>traverseMaster</b>.
|
||||
</dd>
|
||||
<dt>create</dt>
|
||||
<dd>
|
||||
This is the method for creating a PVCopy instance.<br/>
|
||||
<dl>
|
||||
<dt>pvMaster</dt>
|
||||
<dd>the top level structure managed by the server.</dd>
|
||||
<dt>pvRequest</dt>
|
||||
<dd>selects the set of subfields desired
|
||||
and options for each field.</dd>
|
||||
<dt>structureName</dt>
|
||||
<dd>the name for the top level of any PVStructure created.
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt>getPVMaster</dt>
|
||||
<dd>
|
||||
Gets the top level structure from pvMaster.
|
||||
</dd>
|
||||
<dt>traverseMaster</dt>
|
||||
<dd>
|
||||
Traverse all fields of the top level structure of pvMaster.
|
||||
For each field the callback is called.
|
||||
</dd>
|
||||
<dt>getStructure</dt>
|
||||
<dd>
|
||||
Get the introspection interface for a PVStructure for e copy.
|
||||
</dd>
|
||||
<dt>createPVStructure</dt>
|
||||
<dd>Create a copy instance.
|
||||
Monitors keep a queue of monitor elements.
|
||||
Since each element needs a PVStructure, multiple top level structures
|
||||
will be created.
|
||||
</dd>
|
||||
<dt>getCopyOffset</dt>
|
||||
<dd>Given a field in pvMaster.
|
||||
return the offset in copy for the same field.
|
||||
A value of std::string::npos means that the copy does not have this field.
|
||||
Two overloaded methods are provided. The first is called if
|
||||
the field of master is not a structure. The second is for
|
||||
subfields of a structure.
|
||||
</dd>
|
||||
<dt>getMasterPVField</dt>
|
||||
<dd>
|
||||
Given a offset in the copy get the corresponding field in pvMaster.
|
||||
</dd>
|
||||
<dt>initCopy</dt>
|
||||
<dd>
|
||||
Initialize the fields in copyPVStructure
|
||||
by giving each field the value from the corresponding field in pvMaster.
|
||||
bitSet will be set to show that all fields are changed.
|
||||
This means that bit set will have the value <b>{0}</b>.
|
||||
</dd>
|
||||
<dt>updateCopySetBitSet</dt>
|
||||
<dd>
|
||||
Set all fields in copyPVStructure to the value of the corresponding field
|
||||
in pvMaster. Each field that is changed has it's corresponding
|
||||
bit set in bitSet.
|
||||
</dd>
|
||||
<dt>updateCopyFromBitSet</dt>
|
||||
<dd>
|
||||
For each set bit in bitSet set the field in copyPVStructure to the value
|
||||
of the corresponding field in pvMaster.
|
||||
</dd>
|
||||
<dt>updateMaster</dt>
|
||||
<dd>
|
||||
For each set bit in bitSet set the field in pvMaster to the value
|
||||
of the corresponding field in copyPVStructure.
|
||||
|
||||
</dd>
|
||||
<dt>getOptions</dt>
|
||||
<dd>
|
||||
Get the options for the field at the specified offset.
|
||||
A NULL is returned if no options were specified for the field.
|
||||
If options were specified,PVStructurePtr is
|
||||
a structure with a set of PVString subfields that specify name,value
|
||||
pairs. name is the subField name and value is the subField value.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>support for monitor</h2>
|
||||
<p>This consists of two components:
|
||||
<dl>
|
||||
<dt>monitor</dt>
|
||||
<dd>Used by code that implements pvAccess monitors.</dd>
|
||||
<dt>monitorPlugin</dt>
|
||||
<dd>Code that provides special semantics for monitors.</dd>
|
||||
</dl>
|
||||
</p>
|
||||
<h3>monitor</h3>
|
||||
<pre>
|
||||
class MonitorElement {
|
||||
MonitorElement(PVStructurePtr const & pvStructurePtr);
|
||||
PVStructurePtr pvStructurePtr;
|
||||
BitSetPtr changedBitSet;
|
||||
BitSetPtr overrunBitSet;
|
||||
};
|
||||
|
||||
class Monitor {
|
||||
virtual Status start() = 0;
|
||||
virtual Status stop() = 0;
|
||||
virtual MonitorElementPtr poll() = 0;
|
||||
virtual void release(MonitorElementPtr const & monitorElement) = 0;
|
||||
};
|
||||
|
||||
class MonitorRequester : public virtual Requester {
|
||||
virtual void monitorConnect(Status const & status,
|
||||
MonitorPtr const & monitor, StructureConstPtr const & structure) = 0;
|
||||
virtual void monitorEvent(MonitorPtr const & monitor) = 0;
|
||||
virtual void unlisten(MonitorPtr const & monitor) = 0;
|
||||
};
|
||||
</pre>
|
||||
<h4>monitorElement</h4>
|
||||
<p><b>MonitorElement</b> holds the data for one element of a monitor queue.
|
||||
It has the fields:
|
||||
<dl>
|
||||
<dt>pvStructurePtr</dt>
|
||||
<dd>A top level structure with data values at the time the monitors occurs.</dd>
|
||||
<dt>changedBitSet</dt>
|
||||
<dd>Shows which fields have changed since the previous monitor.</dd>
|
||||
<dt>overrunBitSet</dt>
|
||||
<dd>Shows which fields have changed more han once since the previous monitor.</dd>
|
||||
</dl>
|
||||
</p>
|
||||
<h4>monitorElement queue</h4>
|
||||
<p>
|
||||
A queue of monitor elements must be implemented by any channel provider that implements
|
||||
<b>Channel::createMonitor</b>.
|
||||
For an example implementation look at pvDatabaseCPP.
|
||||
It has the following:
|
||||
<pre>
|
||||
typedef Queue<MonitorElement> MonitorElementQueue;
|
||||
typedef std::tr1::shared_ptr<MonitorElementQueue> MonitorElementQueuePtr;
|
||||
|
||||
class MultipleElementQueue :
|
||||
public ElementQueue
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(MultipleElementQueue);
|
||||
virtual ~MultipleElementQueue(){}
|
||||
MultipleElementQueue(
|
||||
MonitorLocalPtr const &monitorLocal,
|
||||
MonitorElementQueuePtr const &queue,
|
||||
size_t nfields);
|
||||
virtual void destroy(){}
|
||||
virtual Status start();
|
||||
virtual Status stop();
|
||||
virtual bool dataChanged();
|
||||
virtual MonitorElementPtr poll();
|
||||
virtual void release(MonitorElementPtr const &monitorElement);
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
<h4>Monitor</h4>
|
||||
<p><b>Monitor</b> must be implemented by any channel provider that implements
|
||||
<b>Channel::createMonitor</b>.
|
||||
Remote PVAccess also implements Monitor on the client side.
|
||||
Note that each client has it's own queue that is not shared with other client.
|
||||
</p>
|
||||
<p>Monitor has the following methods:</p>
|
||||
<dl>
|
||||
<dt>start</dt>
|
||||
<dd>
|
||||
Start monitoring.
|
||||
This will result in a an initial monitor that has the current value
|
||||
of all fields.
|
||||
</dd>
|
||||
<dt>stop</dt>
|
||||
<dd>
|
||||
Stop monitoring.
|
||||
</dd>
|
||||
<dt>poll</dt>
|
||||
<dd>
|
||||
Called to get a monitor element.
|
||||
If no new elements are available then a null pointer is returned.
|
||||
</dd>
|
||||
<dt>release</dt>
|
||||
<dd>
|
||||
Release the monitor element.
|
||||
The caller owns the monitor element between the calls to poll and release.
|
||||
</dd>
|
||||
<dl>
|
||||
</dl>
|
||||
<h4>MonitorRequester</h4>
|
||||
<p>This must be implemented by a pvAccess client.
|
||||
It has the methods:</p>
|
||||
<dl>
|
||||
<dt>monitorConnect</dt>
|
||||
<dd>
|
||||
A monitor has either connected of disconnected.
|
||||
</dd>
|
||||
<dt>monitorEvent</dt>
|
||||
<dd>
|
||||
A new monitor element is available.
|
||||
</dd>
|
||||
<dt>unlisten</dt>
|
||||
<dd>
|
||||
The channel is going away. The client cam no longer access the monitor.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h3>monitorPlugin</h3>
|
||||
<pre>
|
||||
class MonitorPlugin
|
||||
{
|
||||
virtual std::string const & getName() = 0;
|
||||
virtual bool causeMonitor(
|
||||
PVFieldPtr const &pvField,
|
||||
PVStructurePtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement) = 0;
|
||||
virtual void monitorDone(
|
||||
MonitorElementPtr const &monitorElement);
|
||||
virtual void startMonitoring();
|
||||
virtual void stopMonitoring();
|
||||
virtual void beginGroupPut();
|
||||
virtual void endGroupPut();
|
||||
};
|
||||
|
||||
class MonitorPluginCreator
|
||||
{
|
||||
virtual MonitorPluginPtr create(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions) = 0;
|
||||
virtual std::string const & getName() = 0;
|
||||
}
|
||||
|
||||
class MonitorPluginManager
|
||||
{
|
||||
static MonitorPluginManagerPtr get();
|
||||
bool addPlugin(
|
||||
std::string const &pluginName,
|
||||
MonitorPluginCreatorPtr const &creator);
|
||||
MonitorPluginCreatorPtr findPlugin(std::string const &pluginName);
|
||||
void showNames();
|
||||
};
|
||||
|
||||
</pre>
|
||||
<h4>MonitorPlugin</h4>
|
||||
<p><b>MonitorPlugin</b> must be implemented by the plugin implementation.
|
||||
It has methods:</p>
|
||||
<dl>
|
||||
<dt>getName</dt>
|
||||
<dd>Get the name of the plugin.</dd>
|
||||
<dt>causeMonitor</dt>
|
||||
<dd>
|
||||
Should the value of pvField cause a monitor to be raised.
|
||||
pvField and pvTop are fields in the top level structure
|
||||
being monitored. monitorElement has the top level structure
|
||||
for the copy</b>.
|
||||
The implementation should <b>not</b> modify the fields in the structure
|
||||
being monitored.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
<dt>monitorDone</dt>
|
||||
<dd>
|
||||
Called just before monitorElement will be given to client.
|
||||
The plugin can change the data values and bitSets in monitorElement.
|
||||
Called with pvTop unlocked.
|
||||
</dd>
|
||||
<dt>startMonitoring</dt>
|
||||
<dd>
|
||||
Monitoring is starting.
|
||||
</dd>
|
||||
<dt>stopMonitoring</dt>
|
||||
<dd>
|
||||
Monitoring is being stopped.
|
||||
</dd>
|
||||
<dt>beginGroupPut</dt>
|
||||
<dd>
|
||||
A set of puts is starting.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
<dt>endGroupPut</dt>
|
||||
<dd>
|
||||
The set of puts is complete.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
</dl>
|
||||
<h4>MonitorPluginCreator</h4>
|
||||
<p><b>MonitorPluginCreator</b> must also be implemented by the plugin implementation.
|
||||
It is called for each field instance that has options of the from
|
||||
<b>[plugin=name...]</b> where <b>name</b> is the name of the plugin.
|
||||
Note that a plugin instance will belong to a single client.
|
||||
It has methods:</p>
|
||||
<dl>
|
||||
<dt>getName</dt>
|
||||
<dd>Get the name of the plugin.</dd>
|
||||
<dt>create</dt>
|
||||
<dd>
|
||||
Create a new plugin instance.
|
||||
If the arguments are not compatible with the plugin a NULL shared pointer is
|
||||
returned.<br/>
|
||||
pvFieldOptions is
|
||||
a structure with a set of PVString subfields that specify <b>name,value</b>
|
||||
pairs. name is the subField name and value is the subField value.<br/>
|
||||
Note that a plugin will below to a single client.
|
||||
</dd>
|
||||
<dl>
|
||||
<h4>MonitorPluginManager</h4>
|
||||
<p><b>MonitorPluginManager</b> has the methods:</p>
|
||||
<dl>
|
||||
<dt>get</dt>
|
||||
<dd>
|
||||
MonitorPluginManager is a singleton.
|
||||
The first call to get will create the single instance.
|
||||
Further calls will return the single instance.
|
||||
</dd>
|
||||
<dt>addPlugin</dt>
|
||||
<dd>
|
||||
Add a new plugin.
|
||||
</dd>
|
||||
<dt>findPlugin</dt>
|
||||
<dd>
|
||||
Find a plugin. A NULL shared pointer is returned if it has not been added.
|
||||
</dd>
|
||||
<dt>showNames</dt>
|
||||
<dd>
|
||||
Show the names of all plugins that have been added.
|
||||
</dd>
|
||||
</dl>
|
||||
<p><b>NOTE:</b>
|
||||
Should the method <b>causeMonitor</b>
|
||||
have arguments <b>pvField</b> and <b>pvTop</b>
|
||||
be defined so that they can not be modified.
|
||||
This would be possible if the following was defined:
|
||||
<pre>
|
||||
typedef std::tr1::shared_ptr<const PVField> PVFieldConstPtr;
|
||||
typedef std::tr1::shared_ptr<const PVStructure> PVStructureConstPtr;
|
||||
</pre>
|
||||
then the definition for causeMonitor could be:
|
||||
<pre>
|
||||
virtual bool causeMonitor(
|
||||
PVFieldConstPtr const &pvField,
|
||||
PVStructureConstPtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement) = 0;
|
||||
</pre>
|
||||
But just adding these definitions is not sufficient.
|
||||
In addition all methods defined in pvDataCPP must be checked.
|
||||
In particular many of the methods in <b>Convert</b> must have
|
||||
their arguments modified.
|
||||
Big job.
|
||||
</p>
|
||||
<h2>monitorPlugin example</h2>
|
||||
<h3>Example Plugin Overview</h3>
|
||||
<p>This section describes an example plugin that:</p>
|
||||
<ul>
|
||||
<li>Only raises monitors when a field changes value.<br />
|
||||
If no plugin is provided
|
||||
the default is to raise a monitor when a put is issued to a field.</li>
|
||||
<li>Optionally a change will not raise a monitor.<br />
|
||||
The change will, however,
|
||||
appear if a put to another field raise a monitor.</li>
|
||||
</ul>
|
||||
<p>As an example assume that a channel provided by pvAccess has a top level structure
|
||||
that represents a power supply.</p>
|
||||
<pre>
|
||||
structure powerSupply
|
||||
structure alarm
|
||||
structure timeStamp
|
||||
structure power
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
structure voltage
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
structure current
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
</pre>
|
||||
<p>A pvAccess client wants to create a monitor on the powerSupply as follows:
|
||||
The client wants a top level structure that looks like:
|
||||
<pre>
|
||||
structure powerSupply
|
||||
structure alarm
|
||||
structure timeStamp
|
||||
structure power
|
||||
double value
|
||||
structure voltage
|
||||
double value
|
||||
structure current
|
||||
double value
|
||||
</pre>
|
||||
In addition the client wants monitors to occur only when one of the monitored
|
||||
fields changes value but not just because a put occured.
|
||||
Also if only the timeStamp changes value then that should not cause a monitor.
|
||||
</p>
|
||||
<p>The example monitor plugin implements the semantics the
|
||||
client wants. It can be attached to any field via the following options:
|
||||
<pre>
|
||||
[plugin=onChange,raiseMonitor=value]
|
||||
</pre>
|
||||
This plugin will trigger a monitor for the field only if the field changes
|
||||
value. In addition <b>value</b> equals <b>false</b> means do not raise a monitor
|
||||
for changes to this field.
|
||||
But if a change to another field does cause a monitor the change to this field
|
||||
will be passed to the client.
|
||||
</p>
|
||||
<p>
|
||||
Assume that the client has already connected to the channel.
|
||||
The client can then issue the commands:</p>
|
||||
<pre>
|
||||
std::string request("field(alarm[plugin=onChange]");
|
||||
request += ",timeStamp[plugin=onChange,raiseMonitor=false]";
|
||||
request += ",power.value[plugin=onChange";
|
||||
request += ",voltage.value[plugin=onChange";
|
||||
request += ",current.value[plugin=onChange";
|
||||
|
||||
PVStructurePtr pvRequest = createRequest->createRequest(request);
|
||||
|
||||
MonitorPtr monitor = channel->createMonitor(monitorRequester,pvRequest);
|
||||
</pre>
|
||||
<h3>Example Plugin Code</h3>
|
||||
<p>The header file to create the example has the definition:</p>
|
||||
<pre>
|
||||
class ExampleMonitorPlugin{
|
||||
public:
|
||||
static void create();
|
||||
};
|
||||
</pre>
|
||||
<p>The implementation is:</p>
|
||||
<pre>
|
||||
class OnChangePlugin : public MonitorPlugin
|
||||
{
|
||||
public:
|
||||
virtual ~OnChangePlugin(){}
|
||||
OnChangePlugin() {}
|
||||
bool init(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions)
|
||||
{
|
||||
pvField = getPVDataCreate()->createPVField(field);
|
||||
raiseMonitor = true;
|
||||
if(pvFieldOptions!=NULL) {
|
||||
PVStringPtr pvString =
|
||||
pvFieldOptions->getSubField<PVString>("raiseMonitor");
|
||||
if(pvString!=NULL) {
|
||||
std::string value = pvString->get();
|
||||
if(value.compare("false")==0) raiseMonitor = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
virtual std::string &getName(){return pluginName;}
|
||||
virtual bool causeMonitor(
|
||||
PVFieldPtr const &pvNew,
|
||||
PVStructurePtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement)
|
||||
{
|
||||
bool isSame = convert->equals(pvNew,pvField);
|
||||
if(isSame) return false;
|
||||
convert->copy(pvNew,pvField);
|
||||
return raiseMonitor;
|
||||
}
|
||||
private:
|
||||
PVFieldPtr pvField;
|
||||
bool raiseMonitor;
|
||||
};
|
||||
class OnChangePluginCreator : public MonitorPluginCreator
|
||||
{
|
||||
public:
|
||||
virtual std::string &getName(){return pluginName;}
|
||||
virtual MonitorPluginPtr create(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions)
|
||||
{
|
||||
OnChangePluginPtr plugin(new OnChangePlugin());
|
||||
bool result = plugin->init(field,top,pvFieldOptions);
|
||||
if(!result) return MonitorPluginPtr();
|
||||
return plugin;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void ExampleMonitorPlugin::create()
|
||||
{
|
||||
static OnChangePluginCreatorPtr plugin;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
if(plugin==NULL) {
|
||||
plugin = OnChangePluginCreatorPtr(new OnChangePluginCreator());
|
||||
MonitorPluginManager::get()->addPlugin(pluginName,plugin);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
documentation/examples.zip
Normal file
BIN
documentation/examples.zip
Normal file
Binary file not shown.
@@ -1,58 +0,0 @@
|
||||
#ifndef MAINPAGE_H
|
||||
#define MAINPAGE_H
|
||||
/**
|
||||
@mainpage pvDataCPP documentation
|
||||
|
||||
- This module is included in [EPICS Base releases](https://epics-controls.org/resources-and-support/base/) beginning with 7.0.1
|
||||
- It may also be [Downloaded](https://github.com/epics-base/pvDataCPP/releases) and built separately.
|
||||
- @htmlonly <a href="modules.html">API components</a> @endhtmlonly
|
||||
- @ref release_notes
|
||||
|
||||
The epics::pvData namespace.
|
||||
See pv/pvData.h header.
|
||||
|
||||
@code
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/createRequest.h>
|
||||
@endcode
|
||||
|
||||
- Type description epics::pvData::Field and sub-classes
|
||||
- Value container epics::pvData::PVField and sub-classes
|
||||
- POD array handling epics::pvData::shared_vector
|
||||
- pvRequest parsing epics::pvData::createRequest()
|
||||
|
||||
Define a structure type and create a container with default values.
|
||||
|
||||
@code
|
||||
namespace pvd = epics::pvData;
|
||||
pvd::StructureConstPtr stype(pvd::FieldBuilder::begin()
|
||||
->add("fld1", pvd::pvInt)
|
||||
->addNestedStructure("sub")
|
||||
->add("fld2", pvd::pvString)
|
||||
->endNested()
|
||||
->createStructure());
|
||||
|
||||
pvd::PVStructuretPtr value(stype->build());
|
||||
|
||||
value->getSubFieldT<pvd::PVInt>("fld1")->put(4); // store integer 4. would throw if not pvInt
|
||||
value->getSubFieldT<pvd::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
|
||||
@endcode
|
||||
|
||||
is equivalent to the following pseudo-code.
|
||||
|
||||
@code
|
||||
struct stype {
|
||||
pvd::int32 fld1;
|
||||
struct {
|
||||
std::string fld2;
|
||||
} sub;
|
||||
};
|
||||
stype value;
|
||||
value.fld1 = 4;
|
||||
value.fld2 = pvd::castUnsafe<std::string>(4.2);
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
#endif /* MAINPAGE_H */
|
||||
|
||||
5480
documentation/pvDataCPP.html
Normal file
5480
documentation/pvDataCPP.html
Normal file
File diff suppressed because it is too large
Load Diff
179
documentation/pvDataCPPCookbook.txt
Normal file
179
documentation/pvDataCPPCookbook.txt
Normal file
@@ -0,0 +1,179 @@
|
||||
pvDataCPP cookbook
|
||||
------------------
|
||||
|
||||
|
||||
Creating introspection data interfaces
|
||||
|
||||
// create a scalar
|
||||
getFieldCreate()->createScalar(pvDouble);
|
||||
|
||||
// create a scalar array
|
||||
getFieldCreate()->createScalarArray(pvDouble);
|
||||
|
||||
// create a structure
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("enum_t")->
|
||||
add("index", pvDouble)->
|
||||
addArray("choices", pvString)->
|
||||
createStructure();
|
||||
|
||||
// create a structure (cntd.)
|
||||
StructureConstPtr enum_t =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("enum_t")->
|
||||
add("index", pvInt)->
|
||||
addArray("choices", pvString)->
|
||||
createStructure();
|
||||
|
||||
// create a structure (cntd.)
|
||||
StructureConstPtr ntEnum =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("epics:nt/NTEnum:1.0")->
|
||||
add("value", enum_t)->
|
||||
addNestedStructure("timeStamp")->
|
||||
setId("time_t")->
|
||||
add("secondsPastEpoch", pvLong)->
|
||||
add("nanoseconds", pvInt)->
|
||||
add("userTag", pvInt)->
|
||||
endNested()->
|
||||
createStructure();
|
||||
|
||||
// create an union == same as structure
|
||||
|
||||
---
|
||||
|
||||
Creating data containers
|
||||
|
||||
// create a scalar
|
||||
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
|
||||
|
||||
// create a scalar array
|
||||
PVDoubleArray::shared_pointer doubleArrayValue = getPVDataCreate()->createPVScalarArray<PVDouble>();
|
||||
|
||||
// create a structure
|
||||
PVStructure::shared_pointer struct = getPVDataCreate()->createPVStructure(ntEnum);
|
||||
|
||||
// create an union
|
||||
PVUnion::shared_pointer pvUnion = getPVDataCreate()->createPVUnion(unionIF);
|
||||
|
||||
// create a structure array
|
||||
PVStructureArray::shared_pointer structArray = getPVDataCreate()->createPVStructureArray(ntEnum);
|
||||
|
||||
|
||||
// scalar usage
|
||||
PVInt::shared_pointer index = struct->getSubField<PVInt>("value.index");
|
||||
int32 ix = index->get();
|
||||
index->put(3);
|
||||
std::cout << *index << std::endl;
|
||||
|
||||
|
||||
// using <<=, >>= operators to get/set
|
||||
*doubleValue <<= 12.3;
|
||||
|
||||
double val;
|
||||
*doubleValue >>= val;
|
||||
|
||||
|
||||
|
||||
// array usage
|
||||
PVStringArray::shared_pointer choices = struct->getSubField<PVStringArray>("value.choices");
|
||||
|
||||
// use view() to access read-only data
|
||||
PVStringArray::const_svector data(choices->view());
|
||||
for (std::size_t i = 0; i < data.size(); i++)
|
||||
std::cout << data[i] << std::endl;
|
||||
|
||||
// use replace() to put new data
|
||||
PVStringArray::svector newdata;
|
||||
newdata.push_back("zero");
|
||||
newdata.push_back("one");
|
||||
newdata.push_back("two");
|
||||
choices->replace(freeze(newdata));
|
||||
|
||||
// (add more use-cases) here
|
||||
|
||||
// print entire array
|
||||
std::cout << *choices << std::endl;
|
||||
|
||||
// print elmenet at index == 1
|
||||
std::cout << format::array_at(1) << *choices << std::endl;
|
||||
|
||||
----
|
||||
|
||||
Union handling
|
||||
|
||||
|
||||
Union::const_shared_pointer punion =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
add("doubleValue", pvDouble)->
|
||||
add("intValue", pvInt)->
|
||||
createUnion();
|
||||
|
||||
PVUnion::shared_pointer u = getPVDataCreate()->createPVUnion(punion);
|
||||
|
||||
// select and put
|
||||
// this create a new instance of PVDouble (everytime select() is called)
|
||||
PVDouble::shared_pointer doubleValue = u->select<PVDouble>("doubleValue");
|
||||
doubeValue->put(12);
|
||||
// select using index (and direct put)
|
||||
u->select<PVDouble>(0)->put(12);
|
||||
// select using existing PVField (PVUnion stores by-reference)
|
||||
u->set("doubleValue", doubleValue);
|
||||
|
||||
// get selected field name or index
|
||||
std::string selectedFN = u->getSelectedFieldName();
|
||||
int32 selectedIndex = u->getSelectedIndex();
|
||||
|
||||
// get currently selected (knowing it's PVDouble)
|
||||
PVDouble value = u->get<PVDouble>();
|
||||
|
||||
|
||||
|
||||
Variant Union handling
|
||||
|
||||
|
||||
PVUnion::shared_pointer any = getPVDataCreate()->createPVVariantUnion();
|
||||
|
||||
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
|
||||
doubleValue->put(12.8);
|
||||
any->set(doubleValue);
|
||||
|
||||
PVDouble::shared_pointer doubleValue2 = any->get<PVDouble>();
|
||||
|
||||
// variant union work by-reference (pointers match also)
|
||||
// doubleValue.get() == doubleValue2.get()
|
||||
|
||||
|
||||
|
||||
------
|
||||
|
||||
Convert
|
||||
|
||||
|
||||
// convert to int
|
||||
int32 i = doubleValue->getAs<int32>();
|
||||
|
||||
// from int
|
||||
doubleValue->putFrom<int32>(i);
|
||||
|
||||
// from string
|
||||
doubleValue->putFrom<std::string>("12.3");
|
||||
|
||||
// from scalar field
|
||||
doubleValue->assign(pvScalar);
|
||||
|
||||
|
||||
|
||||
// convert to int array
|
||||
PVIntArray::const_svector intData;
|
||||
doubleArrayValue->getAs<int32>(intData);
|
||||
|
||||
// from string array
|
||||
PVStringArray::svector labels;
|
||||
labels.push_back("zero");
|
||||
labels.push_back("one");
|
||||
labels.push_back("two");
|
||||
doubleArrayValue->putFrom<std::string>(labels);
|
||||
|
||||
// from scalar array
|
||||
doubleArrayValue->assign(pvScalarArray);
|
||||
3584
documentation/pvDataCPP_20120927.html
Normal file
3584
documentation/pvDataCPP_20120927.html
Normal file
File diff suppressed because it is too large
Load Diff
3622
documentation/pvDataCPP_20121001.html
Normal file
3622
documentation/pvDataCPP_20121001.html
Normal file
File diff suppressed because it is too large
Load Diff
3622
documentation/pvDataCPP_20121026.html
Normal file
3622
documentation/pvDataCPP_20121026.html
Normal file
File diff suppressed because it is too large
Load Diff
3622
documentation/pvDataCPP_20121212.html
Normal file
3622
documentation/pvDataCPP_20121212.html
Normal file
File diff suppressed because it is too large
Load Diff
3625
documentation/pvDataCPP_20130516.html
Normal file
3625
documentation/pvDataCPP_20130516.html
Normal file
File diff suppressed because it is too large
Load Diff
5537
documentation/pvDataCPP_20140501.html
Normal file
5537
documentation/pvDataCPP_20140501.html
Normal file
File diff suppressed because it is too large
Load Diff
5380
documentation/pvDataCPP_20140708.html
Normal file
5380
documentation/pvDataCPP_20140708.html
Normal file
File diff suppressed because it is too large
Load Diff
5392
documentation/pvDataCPP_20140723.html
Normal file
5392
documentation/pvDataCPP_20140723.html
Normal file
File diff suppressed because it is too large
Load Diff
5478
documentation/pvDataCPP_20141110.html
Normal file
5478
documentation/pvDataCPP_20141110.html
Normal file
File diff suppressed because it is too large
Load Diff
5480
documentation/pvDataCPP_20150623.html
Normal file
5480
documentation/pvDataCPP_20150623.html
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,428 +0,0 @@
|
||||
/**
|
||||
|
||||
@page release_notes Release Notes
|
||||
|
||||
Release 7.1.3 (Apr 2019)
|
||||
========================
|
||||
|
||||
- Fix for array serialization error to/from big endian.
|
||||
https://github.com/epics-base/pvDataCPP/issues/65
|
||||
|
||||
Release 7.1.2 (Mar 2019)
|
||||
========================
|
||||
|
||||
- 7.1.1 tag pushed prematurely.
|
||||
|
||||
Release 7.1.1 (Mar 2019)
|
||||
========================
|
||||
|
||||
- Fixes
|
||||
- Init order issue with StandardField::getStandardField()
|
||||
- Build fix for Visual Studio 2013+
|
||||
|
||||
Release 7.1.0 (Nov 2018)
|
||||
========================
|
||||
|
||||
- Deprecations
|
||||
- BoundedString, BoundedScalarArray, and FixedScalarArray will be removed unless they are fixed.
|
||||
See https://github.com/epics-base/pvDataCPP/issues/52 for discussion.
|
||||
- pv/localStaticLock.h
|
||||
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
|
||||
- Removals
|
||||
- Remove previously deprecated executor.h, queue.h and timerFunction.h
|
||||
- Remove *HashFunction functors to "hash" Field sub-classes which were never fully implemented.
|
||||
- Fixes
|
||||
- Make thread safe getFieldCreate() and getPVDataCreate()
|
||||
- Workaround for MSVC pickyness that iterators be non-NULL, even when not de-referenced.
|
||||
- Fix alignment fault during (de)serialization on RTEMS/vxWorks.
|
||||
- Fix epics::pvData::shared_vector::swap() for void specialization.
|
||||
- Changes in several epics::pvData::Field sub-classes to return const ref. instead of a copy.
|
||||
- Additions
|
||||
- epics::pvData::shared_vector add c++11 move and construct for initializer list.
|
||||
- Add epics::pvData::AnyScalar::clear()
|
||||
- Add ctor epics::pvData::AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
|
||||
- Add epics::pvData::Timer::close()
|
||||
- Allow epics::pvData::castUnsafe() from const char* without first allocating a std::string.
|
||||
- De-duplication of epics::pvData::Field instances is performed using a global hash table.
|
||||
Identical definitions will share a single instance. Allows O(0) comparision.
|
||||
- Add epics::pvData::PVRequestMapper to facilitate (partial) copying between PVStructure instances
|
||||
modified by a pvRequest.
|
||||
- Add shorthand notations epics::pvData::FieldBuilder::begin() and epics::pvData::Field::build()
|
||||
|
||||
Release 7.0.0 (Dec 2017)
|
||||
========================
|
||||
|
||||
- Removals
|
||||
- Remove requester.h, monitor.h, and destroyable.h.. Migrated to the pvAccessCPP module.
|
||||
- Previously deprecated monitorPlugin.h is removed.
|
||||
- Remove pv/messageQueue.h and epics::pvData::MessageQueue
|
||||
- Deprecate the following utility classes, to be removed in 8.0.
|
||||
- epics::pvData::Queue
|
||||
- epics::pvData::Executor
|
||||
- epics::pvData::TimeFunction
|
||||
- Additions
|
||||
- Add pv/pvdVersion.h which is included by pv/pvIntrospect.h
|
||||
- Add epics::pvData::createRequest() function. Alternative to epics::pvData::CreateRequest class which throws on error.
|
||||
- epics::pvData::FieldBuilder allow Structure defintion to be changed/appended
|
||||
- Add epics::pvData::ValueBuilder like FieldBuilder also sets initial values.
|
||||
- Can also be constructed using an existing PVStructure to allow "editing".
|
||||
- Add debugPtr.h wrapper with reference tracking to assist in troubleshooting shared_ptr related ref. loops.
|
||||
- Add @ref pvjson utilities
|
||||
- Add reftrack @ref pvd_reftrack
|
||||
- Add header typemap.h to facilitate boilerplate switch() over ScalarType
|
||||
- Add epics::auto_ptr typedef in help writing code supporting both c++98 and c++11 w/o copious deprecation warnings.
|
||||
|
||||
|
||||
Release 6.0.1
|
||||
=============
|
||||
|
||||
The changes since release 6.0.0 are:
|
||||
|
||||
* Fix "Problem building pvDataCPP for win32-x86-mingw" (issue #42)
|
||||
* In src/misc/bitSet.cpp #include "algorithm" required for MSVS 2015
|
||||
* In testApp/misc/testTypeCast.cpp print (u)int8 values as integers
|
||||
* Minor documentation updates
|
||||
|
||||
|
||||
Release 6.0.0 (Aug. 2016)
|
||||
=======================
|
||||
|
||||
The main changes since release 5.0.4 are:
|
||||
|
||||
* Linux shared library version added
|
||||
* Headers have been moved into pv directories
|
||||
* Bitset functions declared const where possible
|
||||
* Bitset::swap added
|
||||
* Requester::message has default implementation
|
||||
* Serialization/deserialization helpers added
|
||||
* Non-template getSubField char* overload added
|
||||
* MonitorPlugin deprecated
|
||||
* Field name validation performed
|
||||
* Now builds for Cygwin and MinGW targets
|
||||
* Fix for debug build issue.
|
||||
* New license file replaces LICENSE and COPYRIGHT
|
||||
|
||||
Shared library version added
|
||||
----------------------------
|
||||
|
||||
Linux shared library version numbers have been added by setting SHRLIB_VERSION
|
||||
(to 6.0 in this case). So shared object will be libpvData.so.6.0 instead of
|
||||
libpvData.so.
|
||||
|
||||
Headers have been moved into pv directories
|
||||
-------------------------------------------
|
||||
|
||||
E.g. src/property/alarm.h -> src/property/pv/alarm.h
|
||||
|
||||
This facilitates using some IDEs such as Qt Creator.
|
||||
|
||||
Requester::message has default implementation
|
||||
---------------------------------------------
|
||||
|
||||
Requester::message is no longer pure virtual. Default implementation sends
|
||||
string to std::cerr.
|
||||
|
||||
Serialization/deserialization helpers added
|
||||
-------------------------------------------
|
||||
|
||||
A helper function, serializeToVector, has been added which serializes a
|
||||
Serializable object into a standard vector of UInt8s.
|
||||
|
||||
Similarly a function deserializeFromVector deserializes a standard vector into
|
||||
a Deserializable object.
|
||||
|
||||
A function deserializeFromBuffer deserializes a ByteBuffer into a
|
||||
Deserializable object.
|
||||
|
||||
Field name validation performed
|
||||
-------------------------------
|
||||
|
||||
On creating a Structure or Union the field names are now validated.
|
||||
|
||||
Valid characters for a field name are upper or lowercase letters, numbers and
|
||||
underscores and intial numbers are invalid, i.e. names must be of the form
|
||||
[A-Za-z_][A-Za-z0-9_]*.
|
||||
|
||||
Now builds for Cygwin and MinGW targets
|
||||
---------------------------------------
|
||||
|
||||
Includes cross-compiling MinGW on Linux.
|
||||
|
||||
|
||||
Release 5.0.4
|
||||
=============
|
||||
|
||||
The changes since release 5.0.3 are:
|
||||
|
||||
* Fixed bitset serialization (issue #24)
|
||||
* Fixed truncation in BitSet::or_and (issue #27)
|
||||
|
||||
Fixed bitset serialization (issue #24)
|
||||
--------------------------------------
|
||||
|
||||
C++ bitset serialization was not consistent with the C++ deserialization and
|
||||
Java code in some instances (depending on the endianness of the serializer and
|
||||
deserializer) when the number of bits was 56-63 modulo 64. C++ serialization
|
||||
has been fixed.
|
||||
|
||||
Fix exposed issue in deserialization on 32-bit platforms which
|
||||
has also been corrected.
|
||||
|
||||
Fixed truncation in BitSet::or_and (issue #27)
|
||||
----------------------------------------------
|
||||
|
||||
If n, n1 and n2 words are used to store the values of the bitsets bitset,
|
||||
bitset1 and bitset2 respectively then max(n, min(n1,n2)) words are needed
|
||||
to store bitset.or_(bitset1, bitset2).
|
||||
|
||||
Previously min(n1,n2) words were used and the result would be truncated in
|
||||
some instances. This has been fixed.
|
||||
|
||||
|
||||
Release 5.0.3
|
||||
=============
|
||||
|
||||
The only change since release 5.0.2 is:
|
||||
|
||||
Fixed buffer overflow in PVUnion::serialize() (issue #20)
|
||||
---------------------------------------------------------
|
||||
|
||||
A PVUnion whose stored value was null was serialized without checking
|
||||
whether the buffer had sufficient capacity. This has been fixed by calling
|
||||
ensureBuffer().
|
||||
|
||||
|
||||
Release 5.0.2 (Sep. 2015)
|
||||
=========================
|
||||
|
||||
The main changes since release 4.0 are:
|
||||
|
||||
- Deprecated getXXXField() methods have been removed from PVStructure
|
||||
- Convert copy methods and equals operators (re)moved
|
||||
- Convert::copyUnion now always copies between subfields.
|
||||
- New method getSubFieldT, like getSubField except it throws an exception
|
||||
- findSubField method removed from PVStructure
|
||||
- New stream operators for Field and PVField are provided
|
||||
- New template versions of Structure::getField
|
||||
- Fixes for static initialisation order issues
|
||||
- CreateRequest prevents a possible SEGFAULT
|
||||
|
||||
|
||||
Deprecated getXXXField methods have been removed from PVStructure
|
||||
-------------------------------------------------------------------
|
||||
|
||||
The following methods have been removed from PVStructure
|
||||
|
||||
- getBooleanField
|
||||
- getByteField, getShortField, getIntField, getLongField
|
||||
- getUByteField, getUShortField, getUIntField, getULongField
|
||||
- getStringField
|
||||
- getStructureField, getUnionField
|
||||
- getScalarArrayField, getStructureArrayField, getUnionArrayField
|
||||
|
||||
Use template getSubField instead, e.g. use
|
||||
|
||||
getSubField< PVInt >(fieldName)
|
||||
|
||||
in place of
|
||||
|
||||
getIntField(fieldName)
|
||||
|
||||
|
||||
Convert copy methods and equals operators
|
||||
-----------------------------------------
|
||||
|
||||
Convert copy methods where moved and replaced with methods
|
||||
on PVField classes, i.e.
|
||||
|
||||
PVField::copy(const PVField& from)
|
||||
|
||||
Methods
|
||||
|
||||
PVField::copyUnchecked(const PVField& from)
|
||||
|
||||
were added to allow unchecked copies, to gain performance
|
||||
where checked are not needed (anymore).
|
||||
|
||||
In addition:
|
||||
- isCompatibleXXX methods were removed in favour of Field::operator==.
|
||||
- equals methods were remove in favour of PVField::operator==.
|
||||
- operator== methods where moved to pvIntrospect.h and pvData.h
|
||||
|
||||
|
||||
Convert::copyUnion
|
||||
-----------------
|
||||
|
||||
Before this method, depending on types for to and from,
|
||||
sometimes did a shallow copy, i.e. just made to shared_ptr for to
|
||||
share the same data as from.
|
||||
Now it always copies between the subfield of to and from.
|
||||
|
||||
|
||||
New method getSubFieldT, like getSubField except it throws an exception
|
||||
--------------------
|
||||
|
||||
PVStructure has a new template member
|
||||
|
||||
getSubFieldT(std::string const &fieldName)
|
||||
|
||||
that is like `getSubField()` except that it throws a runtime_error
|
||||
instead of returning null.
|
||||
|
||||
|
||||
findSubField method removed from PVStructure
|
||||
--------------------------------------------
|
||||
|
||||
This was mainly used in the implementation of getSubField. With a change to
|
||||
the latter, findSubField was removed.
|
||||
|
||||
|
||||
New stream operators
|
||||
--------------------
|
||||
|
||||
New steam operators are available for Field and PVField.
|
||||
Before to print a Field (or any extension) or a PVField (or any extension)
|
||||
it was necessary to have code like:
|
||||
|
||||
void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
if(struc) {
|
||||
cout << *struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
if(pv) {
|
||||
cout << *.struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
}
|
||||
|
||||
Now it can be done as follows:
|
||||
|
||||
void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
cout << struc << endl;
|
||||
cout << pv << endl;
|
||||
}
|
||||
|
||||
|
||||
New template version of Structure::getField
|
||||
--------------------------------------------
|
||||
|
||||
A new template getField method has been added to Structure
|
||||
|
||||
template<typename FT >
|
||||
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
|
||||
|
||||
Can be used, for example, as follows:
|
||||
|
||||
StructurePtr tsStruc = struc->getField<Structure>("timeStamp");
|
||||
|
||||
|
||||
Fixes for static initialisation order issues
|
||||
--------------------------------------------
|
||||
|
||||
Certain static builds (in particular Windows builds) of applications using
|
||||
pvData had issues due to PVStructure::DEFAULT_ID being used before being initialised. This has been fixed.
|
||||
|
||||
|
||||
CreateRequest change
|
||||
--------------------
|
||||
|
||||
createRequest could cause a SEGFAULT if passed a bad argument.
|
||||
This has been changed so the it returns a null pvStructure
|
||||
and provides an error.
|
||||
|
||||
|
||||
Release 4.0.3
|
||||
=============
|
||||
|
||||
The main changes since release 3.0.2 are:
|
||||
|
||||
- array semantics now enforce Copy On Write.
|
||||
- String no longer defined.
|
||||
- timeStamp and valueAlarm name changes
|
||||
- toString replaced by stream I/O
|
||||
- union is new type.
|
||||
- copy is new.
|
||||
- monitorPlugin is new.
|
||||
|
||||
New Semantics for Arrays
|
||||
--------
|
||||
|
||||
PVScalarArray, PVStructureArray, and PVUnionArray all enforce COW (Copy On Write) Semantics.
|
||||
In order to limit memory usage the storage for raw data is managed via a new shared_vector facility.
|
||||
This allows multiple instances of array data to use the shared raw data.
|
||||
COW is implemented via shared_vectors of const data, i. e. data that can not be modified.
|
||||
|
||||
|
||||
String no longer defined
|
||||
---------
|
||||
|
||||
This is replaced by std::string.
|
||||
|
||||
|
||||
timeStamp and valueAlarm name changes
|
||||
--------------
|
||||
|
||||
In timeStamp nanoSeconds is changed to nanoseconds.
|
||||
|
||||
In valueAlarm hysteresis is changed to hysteresis
|
||||
|
||||
|
||||
toString replaced by stream I/O
|
||||
---------
|
||||
|
||||
pvData.h and pvIntrospect no longer defines toString
|
||||
Instead they have stream support.
|
||||
pvIntrospect uses method dump and pvData uses dumpValue.
|
||||
For example:
|
||||
|
||||
PVDoublePtr pvValue;
|
||||
String buffer;
|
||||
pvValue->toString(&buffer);
|
||||
cout << buffer << endl;
|
||||
buffer.clear();
|
||||
pvValue->getField()->toString(&buffer);
|
||||
cout << buffer << evdl;
|
||||
|
||||
is replaced by
|
||||
|
||||
PVDoublePtr pvValue;
|
||||
cout << *pvValue << endl
|
||||
cout << *pvValue->getField() << endl;
|
||||
|
||||
|
||||
union is a new basic type.
|
||||
------------
|
||||
|
||||
There are two new basic types: union_t and unionArray.
|
||||
|
||||
A union is like a structure that has a single subfield.
|
||||
There are two flavors:
|
||||
|
||||
- *variant union* The field can have any type.
|
||||
- *union* The field can any of specified set of types.
|
||||
|
||||
The field type can be dynamically changed.
|
||||
|
||||
copy
|
||||
----
|
||||
|
||||
This consists of createRequest and pvCopy.
|
||||
createRequest was moved from pvAccess to here.
|
||||
pvCopy is moved from pvDatabaseCPP and now depends
|
||||
only on pvData, i.e. it no longer has any knowledge of PVRecord.
|
||||
|
||||
monitorPlugin
|
||||
-------------
|
||||
|
||||
This is for is for use by code that implements pvAccess monitors.
|
||||
This is prototype and is subject to debate.
|
||||
|
||||
Release 3.0.2
|
||||
==========
|
||||
This was the starting point for RELEASE_NOTES
|
||||
|
||||
*/
|
||||
@@ -1,14 +0,0 @@
|
||||
# Makefile for the examples
|
||||
# make sure they compile
|
||||
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
PROD_LIBS += pvData Com
|
||||
|
||||
TESTPROD_HOST += unittest
|
||||
unittest_SRCS += unittest.cpp
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/* c++ unittest skeleton */
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
namespace {
|
||||
void testCase1() {
|
||||
testEqual(1+1, 2);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
MAIN(testUnitTest)
|
||||
{
|
||||
testPlan(1);
|
||||
try {
|
||||
testCase1();
|
||||
}catch(std::exception& e){
|
||||
PRINT_EXCEPTION(e); // print stack trace if thrown with THROW_EXCEPTION()
|
||||
testAbort("Unhandled exception: %s", e.what());
|
||||
}
|
||||
return testDone();
|
||||
}
|
||||
@@ -6,30 +6,16 @@
|
||||
#
|
||||
# Author: Ralph Lange <ralph.lange@gmx.de>
|
||||
# Copyright (C) 2013 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
|
||||
# Copyright (C) 2014-2016 ITER Organization.
|
||||
# Copyright (C) 2014-2015 ITER Organization.
|
||||
# All rights reserved. Use is subject to license terms.
|
||||
|
||||
installTool () {
|
||||
local module=$1
|
||||
local version=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/${module}-${version}_Build/lastSuccessfulBuild/artifact/${module,,}-${version}.CB-dist.tar.gz
|
||||
tar -xzf ${module,,}-${version}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
installE4 () {
|
||||
local module=$1
|
||||
local branch=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
tar -xzf ${module}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
###########################################
|
||||
# Defaults for EPICS Base
|
||||
# Determine EPICS Base version
|
||||
|
||||
DEFAULT_BASE=3.15.4
|
||||
BASE=${BASE:-${DEFAULT_BASE}}
|
||||
DEFAULT_BASE=3.14.12.5
|
||||
|
||||
BASE=${1:-${DEFAULT_BASE}}
|
||||
USE_MB=${2:-"MB_NO"}
|
||||
|
||||
###########################################
|
||||
# Fetch and unpack dependencies
|
||||
@@ -40,8 +26,8 @@ rm -fr ${STUFF}
|
||||
mkdir -p ${STUFF}
|
||||
cd ${STUFF}
|
||||
|
||||
installTool Boost 1.61.0
|
||||
installTool Base ${BASE}
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/Base-${BASE}_Build/lastSuccessfulBuild/artifact/base-${BASE}.CB-dist.tar.gz
|
||||
tar -xzf base-${BASE}.CB-dist.tar.gz
|
||||
|
||||
###########################################
|
||||
# Build
|
||||
@@ -65,6 +51,6 @@ make distclean all
|
||||
make runtests
|
||||
|
||||
###########################################
|
||||
# Create cache
|
||||
# Create distribution
|
||||
|
||||
tar czf pvData.CB-dist.tar.gz lib include cfg LICENSE
|
||||
tar czf pvData.CB-dist.tar.gz lib include COPYRIGHT LICENSE
|
||||
|
||||
@@ -6,31 +6,14 @@
|
||||
#
|
||||
# Author: Ralph Lange <ralph.lange@gmx.de>
|
||||
# Copyright (C) 2013 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
|
||||
# Copyright (C) 2014-2016 ITER Organization.
|
||||
# Copyright (C) 2014-2015 ITER Organization.
|
||||
# All rights reserved. Use is subject to license terms.
|
||||
|
||||
installTool () {
|
||||
local module=$1
|
||||
local version=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/${module}-${version}_Build/lastSuccessfulBuild/artifact/${module,,}-${version}.CB-dist.tar.gz
|
||||
tar -xzf ${module,,}-${version}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
installE4 () {
|
||||
local module=$1
|
||||
local branch=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
tar -xzf ${module}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
###########################################
|
||||
# Defaults for EPICS Base and parameters
|
||||
# Set EPICS Base version and upload target
|
||||
|
||||
BASE=3.15.4
|
||||
PUBLISH=${PUBLISH:-NO}
|
||||
BRANCH=${BRANCH:-master}
|
||||
BASE=3.15.2
|
||||
PUBLISH=${1:-DONT}
|
||||
|
||||
###########################################
|
||||
# Fetch and unpack dependencies
|
||||
@@ -41,14 +24,16 @@ rm -fr ${STUFF}
|
||||
mkdir -p ${STUFF}
|
||||
cd ${STUFF}
|
||||
|
||||
installTool Doxygen 1.8.11
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/Doxygen-1.8.3_Build/lastSuccessfulBuild/artifact/doxygen-1.8.3.CB-dist.tar.gz
|
||||
tar -xzf doxygen-1.8.3.CB-dist.tar.gz
|
||||
|
||||
###########################################
|
||||
# Generate
|
||||
|
||||
cd ${WORKSPACE}
|
||||
|
||||
installE4 pvData ${BRANCH}
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/pvDataCPP_Build/BASE=${BASE},USE_MB=MB_NO/lastSuccessfulBuild/artifact/pvData.CB-dist.tar.gz
|
||||
tar -xzf pvData.CB-dist.tar.gz
|
||||
|
||||
export PATH=${STUFF}/bin:${PATH}
|
||||
|
||||
@@ -57,7 +42,7 @@ doxygen
|
||||
###########################################
|
||||
# Publish
|
||||
|
||||
if [ "${PUBLISH}" != "NO" ]; then
|
||||
if [ "${PUBLISH}" != "DONT" ]; then
|
||||
# Upload explicit dummy to ensure target directory exists
|
||||
echo "Created by CloudBees Jenkins upload job. Should be deleted as part of the job." > DUMMY
|
||||
rsync -q -e ssh DUMMY epics-jenkins@web.sourceforge.net:/home/project-web/epics-pvdata/htdocs/docbuild/pvDataCPP/${PUBLISH}/
|
||||
|
||||
20
src/Makefile
20
src/Makefile
@@ -3,6 +3,9 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv
|
||||
USR_INCLUDES += -I$(INSTALL_LOCATION)/include
|
||||
|
||||
PVDATA_SRC = $(TOP)/src
|
||||
|
||||
include $(PVDATA_SRC)/misc/Makefile
|
||||
@@ -11,26 +14,11 @@ include $(PVDATA_SRC)/factory/Makefile
|
||||
include $(PVDATA_SRC)/property/Makefile
|
||||
include $(PVDATA_SRC)/copy/Makefile
|
||||
include $(PVDATA_SRC)/pvMisc/Makefile
|
||||
include $(PVDATA_SRC)/json/Makefile
|
||||
include $(PVDATA_SRC)/monitor/Makefile
|
||||
|
||||
LIBRARY = pvData
|
||||
|
||||
pvData_LIBS += Com
|
||||
|
||||
EXPANDVARS += EPICS_PVD_MAJOR_VERSION
|
||||
EXPANDVARS += EPICS_PVD_MINOR_VERSION
|
||||
EXPANDVARS += EPICS_PVD_MAINTENANCE_VERSION
|
||||
EXPANDVARS += EPICS_PVD_DEVELOPMENT_FLAG
|
||||
|
||||
EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))")
|
||||
|
||||
# shared library ABI version.
|
||||
SHRLIB_VERSION = $(EPICS_PVD_MAJOR_VERSION).$(EPICS_PVD_MINOR_VERSION).$(EPICS_PVD_MAINTENANCE_VERSION)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
# Can't use EXPAND as generated headers must appear
|
||||
# in O.Common, but EXPAND emits rules for O.$(T_A)
|
||||
../O.Common/pv/pvdVersionNum.h: ../pv/pvdVersionNum.h@
|
||||
$(MKDIR) $(COMMON_DIR)/pv
|
||||
$(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
SRC_DIRS += $(PVDATA_SRC)/copy
|
||||
|
||||
INC += pv/createRequest.h
|
||||
INC += pv/pvCopy.h
|
||||
INC += createRequest.h
|
||||
INC += pvCopy.h
|
||||
|
||||
LIBSRCS += createRequest.cpp
|
||||
LIBSRCS += requestmapper.cpp
|
||||
LIBSRCS += pvCopy.cpp
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include <pv/pvData.h>
|
||||
@@ -22,13 +21,13 @@ using std::endl;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
static FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
|
||||
struct CreateRequestImpl {
|
||||
class CreateRequestImpl : public CreateRequest {
|
||||
private:
|
||||
|
||||
struct Node
|
||||
{
|
||||
@@ -54,7 +53,12 @@ struct CreateRequestImpl {
|
||||
string fullFieldName;
|
||||
|
||||
|
||||
CreateRequestImpl() {}
|
||||
public:
|
||||
CreateRequestImpl()
|
||||
{
|
||||
fullFieldName = "";
|
||||
}
|
||||
private:
|
||||
|
||||
|
||||
void removeBlanks(string& str)
|
||||
@@ -70,7 +74,8 @@ struct CreateRequestImpl {
|
||||
size_t openBrace = request.find('{', index+1);
|
||||
size_t closeBrace = request.find('}', index+1);
|
||||
if(openBrace == string::npos && closeBrace == string::npos){
|
||||
throw std::runtime_error(request + " mismatched {}");
|
||||
message = request + " mismatched {}";
|
||||
throw std::logic_error("message");
|
||||
}
|
||||
if (openBrace != string::npos && openBrace!=0) {
|
||||
if(openBrace<closeBrace) return findMatchingBrace(request,openBrace,numOpen+1);
|
||||
@@ -85,12 +90,14 @@ struct CreateRequestImpl {
|
||||
for(size_t i=index+1; i< request.size(); ++i) {
|
||||
if(request[i] == ']') {
|
||||
if(i==index+1) {
|
||||
throw std::runtime_error(request + " mismatched []");
|
||||
message = request + " mismatched []";
|
||||
throw std::logic_error("message");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(request + " missing ]");
|
||||
message = request + " missing ]";
|
||||
throw std::logic_error("message");
|
||||
}
|
||||
|
||||
size_t findEndField(string& request) {
|
||||
@@ -145,7 +152,7 @@ struct CreateRequestImpl {
|
||||
string const & request)
|
||||
{
|
||||
if(request.length()<=1) {
|
||||
throw std::runtime_error("logic error empty options");
|
||||
throw std::logic_error("logic error empty options");
|
||||
}
|
||||
vector<Node> top;
|
||||
vector<string> items = split(request);
|
||||
@@ -155,7 +162,8 @@ struct CreateRequestImpl {
|
||||
string item = items[j];
|
||||
size_t equals = item.find('=');
|
||||
if(equals==string::npos || equals==0) {
|
||||
throw std::runtime_error(item + " illegal option " + request);
|
||||
message = item + " illegal option " + request;
|
||||
throw std::logic_error("message");
|
||||
}
|
||||
top.push_back(Node(item.substr(0,equals)));
|
||||
string name = fullFieldName + "._options." + item.substr(0,equals);
|
||||
@@ -204,7 +212,7 @@ struct CreateRequestImpl {
|
||||
if(end==0) end = request.size();
|
||||
string name = request.substr(0,end);
|
||||
if(name.size()<1) {
|
||||
throw std::runtime_error("null field name " + request);
|
||||
throw std::logic_error("null field name " + request);
|
||||
}
|
||||
string saveFullName = fullFieldName;
|
||||
fullFieldName += "." + name;
|
||||
@@ -227,7 +235,7 @@ struct CreateRequestImpl {
|
||||
if(chr=='.') {
|
||||
request = request.substr(end+1);
|
||||
if(request.size()==string::npos || request.size()<1) {
|
||||
throw std::runtime_error("null field name " + request);
|
||||
throw std::logic_error("null field name " + request);
|
||||
}
|
||||
Node subNode(name);
|
||||
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
|
||||
@@ -248,11 +256,11 @@ struct CreateRequestImpl {
|
||||
if(chr=='{') {
|
||||
size_t endBrace = findEndField(request);
|
||||
if((end+1)>=(endBrace-1)) {
|
||||
throw std::runtime_error("illegal syntax " + request);
|
||||
throw std::logic_error("illegal syntax " + request);
|
||||
}
|
||||
string subRequest = request.substr(end+1,endBrace-1 -end -1);
|
||||
if(subRequest.size()<1) {
|
||||
throw std::runtime_error("empty {} " + request);
|
||||
throw std::logic_error("empty {} " + request);
|
||||
}
|
||||
Node subNode(name);
|
||||
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
|
||||
@@ -268,7 +276,7 @@ struct CreateRequestImpl {
|
||||
createSubNode(node,request);
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("logic error");
|
||||
throw std::logic_error("logic error");
|
||||
}
|
||||
|
||||
FieldConstPtr createSubStructure(vector<Node> & nodes)
|
||||
@@ -309,16 +317,17 @@ struct CreateRequestImpl {
|
||||
return structure;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
PVStructurePtr createRequest(
|
||||
virtual PVStructurePtr createRequest(
|
||||
string const & crequest)
|
||||
{
|
||||
{
|
||||
try {
|
||||
string request = crequest;
|
||||
if (!request.empty()) removeBlanks(request);
|
||||
if (request.empty())
|
||||
{
|
||||
return fieldCreate->createStructure()->build();
|
||||
return pvDataCreate->createPVStructure(fieldCreate->createStructure());
|
||||
}
|
||||
size_t offsetRecord = request.find("record[");
|
||||
size_t offsetField = request.find("field(");
|
||||
@@ -347,17 +356,20 @@ struct CreateRequestImpl {
|
||||
if(numParan!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched () " << numParan;
|
||||
throw std::runtime_error(oss.str());
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(numBrace!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched {} " << numBrace;
|
||||
throw std::runtime_error(oss.str());
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(numBracket!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched [] " << numBracket;
|
||||
throw std::runtime_error(oss.str());
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
vector<Node> top;
|
||||
try {
|
||||
@@ -366,8 +378,9 @@ struct CreateRequestImpl {
|
||||
size_t openBracket = request.find('[', offsetRecord);
|
||||
size_t closeBracket = request.find(']', openBracket);
|
||||
if(closeBracket==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetRecord) +
|
||||
"record[ does not have matching ]");
|
||||
message = request.substr(offsetRecord) +
|
||||
"record[ does not have matching ]";
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(closeBracket-openBracket > 3) {
|
||||
Node node("record");
|
||||
@@ -383,8 +396,9 @@ struct CreateRequestImpl {
|
||||
size_t openParan = request.find('(', offsetField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " field( does not have matching )");
|
||||
message = request.substr(offsetField)
|
||||
+ " field( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
@@ -397,8 +411,9 @@ struct CreateRequestImpl {
|
||||
size_t openParan = request.find('(', offsetGetField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " getField( does not have matching )");
|
||||
message = request.substr(offsetField)
|
||||
+ " getField( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
@@ -411,8 +426,9 @@ struct CreateRequestImpl {
|
||||
size_t openParan = request.find('(', offsetPutField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " putField( does not have matching )");
|
||||
message = request.substr(offsetField)
|
||||
+ " putField( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
@@ -420,7 +436,9 @@ struct CreateRequestImpl {
|
||||
top.push_back(node);
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
throw std::runtime_error(std::string("while creating Structure exception ")+e.what());
|
||||
string xxx = e.what();
|
||||
message = "while creating Structure exception " + xxx;
|
||||
return PVStructurePtr();
|
||||
}
|
||||
size_t num = top.size();
|
||||
StringArray names(num);
|
||||
@@ -437,7 +455,7 @@ struct CreateRequestImpl {
|
||||
}
|
||||
StructureConstPtr structure = fieldCreate->createStructure(names, fields);
|
||||
if(!structure) throw std::invalid_argument("bad request " + crequest);
|
||||
PVStructurePtr pvStructure = structure->build();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
|
||||
for(size_t i=0; i<optionList.size(); ++i) {
|
||||
OptionPair pair = optionList[i];
|
||||
string name = pair.name;
|
||||
@@ -448,39 +466,20 @@ struct CreateRequestImpl {
|
||||
}
|
||||
optionList.clear();
|
||||
return pvStructure;
|
||||
} catch (std::exception &e) {
|
||||
message = e.what();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics {namespace pvData {
|
||||
|
||||
CreateRequest::shared_pointer CreateRequest::create()
|
||||
{
|
||||
CreateRequest::shared_pointer createRequest(new CreateRequest());
|
||||
CreateRequest::shared_pointer createRequest(new CreateRequestImpl());
|
||||
return createRequest;
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer CreateRequest::createRequest(std::string const & request)
|
||||
{
|
||||
message.clear();
|
||||
try {
|
||||
return ::createRequest(request);
|
||||
} catch(std::exception& e) {
|
||||
message = e.what();
|
||||
return PVStructure::shared_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer createRequest(std::string const & request)
|
||||
{
|
||||
CreateRequestImpl I;
|
||||
return I.createRequest(request);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
}}
|
||||
|
||||
|
||||
60
src/copy/createRequest.h
Normal file
60
src/copy/createRequest.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*createRequest.h*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#ifndef CREATEREQUEST_H
|
||||
#define CREATEREQUEST_H
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/lock.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
/**
|
||||
* @brief Create pvRequest structure for Channel methods.
|
||||
*
|
||||
* Many methods of the Channel class of pvAccess have an
|
||||
* argument <b>PVStructurePtr const * pvRequest</b>.
|
||||
* This class provides a method that creates a valid pvRequest.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass CreateRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(CreateRequest);
|
||||
/**
|
||||
* Create s new instance of CreateRequest
|
||||
* @returns A shared pointer to the new instance.
|
||||
*/
|
||||
static CreateRequest::shared_pointer create();
|
||||
virtual ~CreateRequest() {};
|
||||
/**
|
||||
* Create a request structure for the create calls in Channel.
|
||||
* See the package overview documentation for details.
|
||||
* @param request The field request. See the package overview documentation for details.
|
||||
* @return The request PVStructure if a valid request was given.
|
||||
* If a NULL PVStructure is returned then getMessage will return
|
||||
* the reason.
|
||||
*/
|
||||
virtual PVStructure::shared_pointer createRequest(std::string const & request) = 0;
|
||||
/**
|
||||
* Get the error message of createRequest returns NULL
|
||||
* return the error message
|
||||
*/
|
||||
std::string getMessage() {return message;}
|
||||
protected:
|
||||
CreateRequest() {}
|
||||
std::string message;
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
|
||||
#endif /* CREATEREQUEST_H */
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
/*createRequest.h*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#ifndef CREATEREQUEST_H
|
||||
#define CREATEREQUEST_H
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class BitSet;
|
||||
|
||||
/**
|
||||
* @brief Create pvRequest structure for Channel methods.
|
||||
*
|
||||
* Many methods of the Channel class of pvAccess have an
|
||||
* argument <b>PVStructurePtr const * pvRequest</b>.
|
||||
* This class provides a method that creates a valid pvRequest.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass CreateRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(CreateRequest);
|
||||
/**
|
||||
* Create s new instance of CreateRequest
|
||||
* @returns A shared pointer to the new instance.
|
||||
*/
|
||||
static CreateRequest::shared_pointer create();
|
||||
~CreateRequest() {};
|
||||
/**
|
||||
* Create a request structure for the create calls in Channel.
|
||||
* See the package overview documentation for details.
|
||||
* @param request The field request. See the package overview documentation for details.
|
||||
* @return The request PVStructure if a valid request was given.
|
||||
* If a NULL PVStructure is returned then getMessage will return
|
||||
* the reason.
|
||||
*/
|
||||
PVStructure::shared_pointer createRequest(std::string const & request);
|
||||
/**
|
||||
* Get the error message of createRequest returns NULL
|
||||
* return the error message
|
||||
*/
|
||||
std::string getMessage() {return message;}
|
||||
protected:
|
||||
CreateRequest() {}
|
||||
std::string message;
|
||||
};
|
||||
|
||||
/** Parse and build pvRequest structure.
|
||||
*
|
||||
@params request the Request string to be parsed. eg. "field(value)"
|
||||
@returns The resulting strucuture. Never NULL
|
||||
@throws std::exception for various parsing errors
|
||||
*/
|
||||
epicsShareExtern
|
||||
PVStructure::shared_pointer createRequest(std::string const & request);
|
||||
|
||||
/** Helper for implementations of epics::pvAccess::ChannelProvider in interpreting the
|
||||
* 'field' substructure of a pvRequest.
|
||||
* Copies between an internal (base) Structure, and a client/user visible (requested) Structure.
|
||||
*
|
||||
* @note PVRequestMapper is not re-entrant. It is copyable and swap()able.
|
||||
*/
|
||||
class epicsShareClass PVRequestMapper {
|
||||
public:
|
||||
enum mode_t {
|
||||
/** Masking mode.
|
||||
*
|
||||
* Requested Structure is identical to Base.
|
||||
* The 'field' substructure of the provided pvRequest is used to construct a BitSet
|
||||
* where the bits corresponding to the "selected" fields are set. This mask can be
|
||||
* access via. requestedMask(). The copy* and mask* methods operate only
|
||||
* on "selected" fields.
|
||||
*/
|
||||
Mask,
|
||||
/** Slice mode
|
||||
*
|
||||
* The Requested Structure is a strict sub-set of the Base Structure containing
|
||||
* those fields "selected" by the 'field' substructure of the provided pvRequest.
|
||||
*/
|
||||
Slice,
|
||||
};
|
||||
|
||||
PVRequestMapper();
|
||||
//! @see compute()
|
||||
PVRequestMapper(const PVStructure& base,
|
||||
const PVStructure& pvRequest,
|
||||
mode_t mode = Mask);
|
||||
|
||||
//! return to state of default ctor
|
||||
void reset();
|
||||
|
||||
//! @returns the Structure of the PVStructure previously passed to compute(). NULL if never computed()'d
|
||||
inline const StructureConstPtr& base() const { return typeBase; }
|
||||
//! @returns the Structure which is the selected sub-set of the base Structure. NULL if never computed()'d
|
||||
inline const StructureConstPtr& requested() const { return typeRequested; }
|
||||
|
||||
/** A mask of all fields in the base structure which are also in the requested structure,
|
||||
* and any parent/structure "compress" bits. eg. bit 0 is always set.
|
||||
*
|
||||
@code
|
||||
PVRequestMapper mapper(...);
|
||||
...
|
||||
BitSet changed = ...; // a base changed mask
|
||||
bool wouldcopy = changed.logical_and(mapper.requestedMask());
|
||||
// wouldcopy==false means that copyBaseToRequested(..., changed, ...) would be a no-op
|
||||
@endcode
|
||||
*
|
||||
* eg. allows early detection of empty monitor updates.
|
||||
*/
|
||||
inline const BitSet& requestedMask() const { return maskRequested; }
|
||||
|
||||
//! @returns A new instance of the requested() Structure
|
||||
PVStructurePtr buildRequested() const;
|
||||
//! @returns A new instance of the base() Structure
|
||||
PVStructurePtr buildBase() const;
|
||||
|
||||
/** (re)compute the selected subset of provided base structure.
|
||||
* @param base A full base structure.
|
||||
* Must be "top level" (field offset zero).
|
||||
* @param pvRequest The user/client provided request modifier
|
||||
* @param mode Control how the mapping is constructed. @see mode_t for a description of mapping modes.
|
||||
*
|
||||
* @post Updates warnings()
|
||||
* @throws std::runtime_error For errors involving invalid pvRequest
|
||||
* @throws std::logic_error if the provided base is not a "top level" PVStructure.
|
||||
*/
|
||||
void compute(const PVStructure& base,
|
||||
const PVStructure& pvRequest,
|
||||
mode_t mode = Mask);
|
||||
|
||||
//! After compute(), check if !warnings().empty()
|
||||
inline const std::string& warnings() const { return messages; }
|
||||
|
||||
/** Copy field values from Base structure into Requested structure
|
||||
*
|
||||
* @param base An instance of the base Structure. Field values are copied from it.
|
||||
* Need not be the same instance passed to compute().
|
||||
* @param baseMask A bit mask selecting those base fields to copy.
|
||||
* @param request An instance of the requested() Structure. Field values are copied to it.
|
||||
* @param requestMask A bit mask indicating which requested fields were copied.
|
||||
* BitSet::clear() is not called.
|
||||
*/
|
||||
void copyBaseToRequested(
|
||||
const PVStructure& base,
|
||||
const BitSet& baseMask,
|
||||
PVStructure& request,
|
||||
BitSet& requestMask
|
||||
) const;
|
||||
|
||||
/** Copy field values into Base structure from Requested structure
|
||||
*
|
||||
* @param base An instance of the base Structure. Field values are copied into it.
|
||||
* Need not be the same instance passed to compute().
|
||||
* @param baseMask A bit mask indicating which base fields were copied.
|
||||
* BitSet::clear() is not called.
|
||||
* @param request An instance of the requested() Structure. Field values are copied from it.
|
||||
* @param requestMask A bit mask selecting those requested fields to copy.
|
||||
*/
|
||||
void copyBaseFromRequested(
|
||||
PVStructure& base,
|
||||
BitSet& baseMask,
|
||||
const PVStructure& request,
|
||||
const BitSet& requestMask
|
||||
) const;
|
||||
|
||||
//! Translate Base bit mask into requested bit mask.
|
||||
//! BitSet::clear() is not called.
|
||||
inline void maskBaseToRequested(
|
||||
const BitSet& baseMask,
|
||||
BitSet& requestMask
|
||||
) const
|
||||
{ _mapMask(baseMask, requestMask, false); }
|
||||
|
||||
//! Translate requested bit mask into base bit mask.
|
||||
//! BitSet::clear() is not called.
|
||||
inline void maskBaseFromRequested(
|
||||
BitSet& baseMask,
|
||||
const BitSet& requestMask
|
||||
) const
|
||||
{ _mapMask(requestMask, baseMask, true); }
|
||||
|
||||
//! Exchange contents of two mappers. O(0) and never throws.
|
||||
void swap(PVRequestMapper& other);
|
||||
|
||||
private:
|
||||
bool _compute(const PVStructure& base, const PVStructure& pvReq,
|
||||
FieldBuilderPtr& builder, bool keepids, unsigned depth);
|
||||
|
||||
void _map(const PVStructure& src,
|
||||
const BitSet& maskSrc,
|
||||
PVStructure& dest,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const;
|
||||
void _mapMask(const BitSet& maskSrc,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const;
|
||||
|
||||
StructureConstPtr typeBase, typeRequested;
|
||||
BitSet maskRequested;
|
||||
// Map between field offsets of base and requested Structures.
|
||||
// Include all fields, both leaf and sub-structure.
|
||||
struct Mapping {
|
||||
size_t to; // offset in destination Structure
|
||||
BitSet tomask, // if !leaf these are the other bits in the destination mask to changed
|
||||
frommask; // if !leaf these are the other bits in the source mask to be copied
|
||||
bool valid; // only true in (sparse) base -> requested mapping
|
||||
bool leaf; // not a (sub)Structure?
|
||||
Mapping() :valid(false) {}
|
||||
Mapping(size_t to, bool leaf) :to(to), valid(true), leaf(leaf) {}
|
||||
};
|
||||
typedef std::vector<Mapping> mapping_t;
|
||||
mapping_t base2req, req2base;
|
||||
|
||||
std::string messages;
|
||||
|
||||
mutable BitSet scratch; // avoid temporary allocs. (we aren't re-entrant!)
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* CREATEREQUEST_H */
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* pvCopy.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
@@ -12,11 +13,6 @@
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <compilerDependencies.h>
|
||||
#undef EPICS_DEPRECATED
|
||||
#define EPICS_DEPRECATED
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include <pv/thread.h>
|
||||
@@ -44,6 +40,13 @@ static void newLine(string *buffer, int indentLevel)
|
||||
*buffer += string(indentLevel*4, ' ');
|
||||
}
|
||||
|
||||
static PVCopyPtr NULLPVCopy;
|
||||
static FieldConstPtr NULLField;
|
||||
static StructureConstPtr NULLStructure;
|
||||
static PVStructurePtr NULLPVStructure;
|
||||
static CopyNodePtr NULLCopyNode;
|
||||
static CopyMasterNodePtr NULLCopyMasterNode;
|
||||
|
||||
struct CopyNode {
|
||||
CopyNode()
|
||||
: isStructure(false),
|
||||
@@ -76,12 +79,12 @@ PVCopyPtr PVCopy::create(
|
||||
if(structureName.size()>0) {
|
||||
if(pvRequest->getStructure()->getNumberFields()>0) {
|
||||
pvStructure = pvRequest->getSubField<PVStructure>(structureName);
|
||||
if(!pvStructure) return PVCopyPtr();
|
||||
if(!pvStructure) return NULLPVCopy;
|
||||
}
|
||||
} else if(pvStructure->getSubField<PVStructure>("field")) {
|
||||
} else if(pvStructure->getSubField("field")) {
|
||||
pvStructure = pvRequest->getSubField<PVStructure>("field");
|
||||
}
|
||||
PVCopyPtr pvCopy(new PVCopy(pvMaster));
|
||||
PVCopyPtr pvCopy = PVCopyPtr(new PVCopy(pvMaster));
|
||||
bool result = pvCopy->init(pvStructure);
|
||||
if(!result) pvCopy.reset();
|
||||
return pvCopy;
|
||||
@@ -143,7 +146,7 @@ PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
|
||||
while(true) {
|
||||
if(!node->isStructure) {
|
||||
if(node->structureOffset==fieldOffset) return node->options;
|
||||
return PVStructurePtr();
|
||||
return NULLPVStructure;
|
||||
}
|
||||
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structNode->nodes;
|
||||
@@ -154,7 +157,7 @@ PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
|
||||
if(fieldOffset>=soff && fieldOffset<soff+node->nfields) {
|
||||
if(fieldOffset==soff) return node->options;
|
||||
if(!node->isStructure) {
|
||||
return PVStructurePtr();
|
||||
return NULLPVStructure;
|
||||
}
|
||||
okToContinue = true;
|
||||
break;
|
||||
@@ -345,7 +348,7 @@ bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest)
|
||||
if(len==string::npos) entireMaster = true;
|
||||
if(len==0) entireMaster = true;
|
||||
PVStructurePtr pvOptions;
|
||||
if(len==1) {
|
||||
if(len==1 && pvRequest->getSubField("_options")) {
|
||||
pvOptions = pvRequest->getSubField<PVStructure>("_options");
|
||||
}
|
||||
if(entireMaster) {
|
||||
@@ -388,7 +391,7 @@ StructureConstPtr PVCopy::createStructure(
|
||||
PVFieldPtrArray const &pvFromRequestFields = pvFromRequest->getPVFields();
|
||||
StringArray const &fromRequestFieldNames = pvFromRequest->getStructure()->getFieldNames();
|
||||
size_t length = pvFromRequestFields.size();
|
||||
if(length==0) return StructureConstPtr();
|
||||
if(length==0) return NULLStructure;
|
||||
FieldConstPtrArray fields; fields.reserve(length);
|
||||
StringArray fieldNames; fields.reserve(length);
|
||||
for(size_t i=0; i<length; ++i) {
|
||||
@@ -418,7 +421,7 @@ StructureConstPtr PVCopy::createStructure(
|
||||
fields.push_back(field);
|
||||
}
|
||||
size_t numsubfields = fields.size();
|
||||
if(numsubfields==0) return StructureConstPtr();
|
||||
if(numsubfields==0) return NULLStructure;
|
||||
return getFieldCreate()->createStructure(fieldNames, fields);
|
||||
}
|
||||
|
||||
@@ -438,8 +441,11 @@ CopyNodePtr PVCopy::createStructureNodes(
|
||||
PVFieldPtr copyPVField = copyPVFields[i];
|
||||
string fieldName = copyPVField->getFieldName();
|
||||
|
||||
PVStructurePtr requestPVStructure = pvFromRequest->getSubField<PVStructure>(fieldName);
|
||||
PVStructurePtr pvSubFieldOptions = requestPVStructure->getSubField<PVStructure>("_options");
|
||||
PVStructurePtr requestPVStructure = static_pointer_cast<PVStructure>(
|
||||
pvFromRequest->getSubField(fieldName));
|
||||
PVStructurePtr pvSubFieldOptions;
|
||||
PVFieldPtr pvField = requestPVStructure->getSubField("_options");
|
||||
if(pvField) pvSubFieldOptions = static_pointer_cast<PVStructure>(pvField);
|
||||
PVFieldPtr pvMasterField;
|
||||
PVFieldPtrArray const & pvMasterFields = pvMasterStructure->getPVFields();
|
||||
for(size_t j=0; i<pvMasterFields.size(); j++ ) {
|
||||
@@ -545,7 +551,7 @@ void PVCopy::updateStructureNodeFromBitSet(
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i<nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
PVFieldPtr pvField = pvCopy->getSubFieldT(node->structureOffset);
|
||||
PVFieldPtr pvField = pvCopy->getSubField(node->structureOffset);
|
||||
if(node->isStructure) {
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
|
||||
CopyStructureNodePtr subStructureNode =
|
||||
@@ -626,7 +632,7 @@ CopyMasterNodePtr PVCopy::getCopyOffset(
|
||||
if(masterNode) return masterNode;
|
||||
}
|
||||
}
|
||||
return CopyMasterNodePtr();
|
||||
return NULLCopyMasterNode;
|
||||
}
|
||||
|
||||
CopyMasterNodePtr PVCopy::getMasterNode(
|
||||
@@ -646,7 +652,7 @@ CopyMasterNodePtr PVCopy::getMasterNode(
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
return getMasterNode(subNode,structureOffset);
|
||||
}
|
||||
return CopyMasterNodePtr();
|
||||
return NULLCopyMasterNode;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* pvCopy.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
@@ -13,7 +14,6 @@
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#include <compilerDependencies.h>
|
||||
#include <shareLib.h>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
@@ -59,7 +59,7 @@ typedef std::tr1::shared_ptr<CopyStructureNode> CopyStructureNodePtr;
|
||||
* Class that manages one or more PVStructures that holds an arbitrary subset of the fields
|
||||
* in another PVStructure called master.
|
||||
*/
|
||||
class epicsShareClass EPICS_DEPRECATED PVCopy :
|
||||
class epicsShareClass PVCopy :
|
||||
public std::tr1::enable_shared_from_this<PVCopy>
|
||||
{
|
||||
public:
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
PVStructurePtr const &pvRequest,
|
||||
std::string const & structureName);
|
||||
virtual ~PVCopy(){}
|
||||
void destroy();
|
||||
virtual void destroy();
|
||||
/**
|
||||
* Get the top-level structure of master
|
||||
* @returns The master top-level structure.
|
||||
@@ -1,328 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsTypes.h>
|
||||
#include <epicsVersion.h>
|
||||
#include <epicsConvert.h>
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/createRequest.h>
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
// Our arbitrary limit on pvRequest structure depth to bound stack usage during recursion
|
||||
static const unsigned maxDepth = 5;
|
||||
|
||||
namespace epics{namespace pvData {
|
||||
|
||||
PVRequestMapper::PVRequestMapper() {}
|
||||
|
||||
PVRequestMapper::PVRequestMapper(const PVStructure &base,
|
||||
const PVStructure &pvRequest,
|
||||
mode_t mode)
|
||||
{
|
||||
compute(base, pvRequest, mode);
|
||||
}
|
||||
|
||||
PVStructurePtr PVRequestMapper::buildRequested() const
|
||||
{
|
||||
if(!typeRequested)
|
||||
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
|
||||
return typeRequested->build();
|
||||
}
|
||||
|
||||
PVStructurePtr PVRequestMapper::buildBase() const
|
||||
{
|
||||
if(!typeBase)
|
||||
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
|
||||
return typeBase->build();
|
||||
}
|
||||
|
||||
void PVRequestMapper::compute(const PVStructure &base,
|
||||
const PVStructure &pvRequest,
|
||||
mode_t mode)
|
||||
{
|
||||
if(base.getFieldOffset()!=0)
|
||||
THROW_EXCEPTION2(std::logic_error, "Mapper must be used with top level PVStructure");
|
||||
|
||||
bool ok = true;
|
||||
|
||||
// we want to be transactional, which requires a second copy of everything.
|
||||
PVRequestMapper temp;
|
||||
|
||||
// whether to preserve IDs of partial structures.
|
||||
bool keepids = false;
|
||||
PVScalar::const_shared_pointer pbp(pvRequest.getSubField<PVScalar>("record._options.keepIDs"));
|
||||
try {
|
||||
if(pbp) keepids = pbp->getAs<boolean>();
|
||||
}catch(std::runtime_error& e){
|
||||
std::ostringstream msg;
|
||||
msg<<"Can't parse keepIDs : '"<<e.what()<<"' ";
|
||||
temp.messages+=msg.str();
|
||||
}
|
||||
|
||||
PVStructure::const_shared_pointer fields(pvRequest.getSubField<PVStructure>("field"));
|
||||
if(!fields || fields->getPVFields().empty()) {
|
||||
// not selection, or empty selection, treated as select all
|
||||
temp.typeBase = temp.typeRequested = base.getStructure();
|
||||
|
||||
for(size_t i=1, N=base.getNextFieldOffset(); i<N; i++)
|
||||
temp.maskRequested.set(i);
|
||||
|
||||
} else {
|
||||
FieldBuilderPtr builder(getFieldCreate()->createFieldBuilder());
|
||||
|
||||
if(keepids)
|
||||
builder = builder->setId(base.getStructure()->getID());
|
||||
|
||||
ok &= temp._compute(base, *fields, builder, keepids, 0); // fills in builder
|
||||
|
||||
temp.typeBase = base.getStructure();
|
||||
temp.typeRequested = builder->createStructure();
|
||||
// possible that typeBase==typeRequested if all fields explicitly selected
|
||||
}
|
||||
|
||||
if(mode==Mask) {
|
||||
// short circuit use of masked Structure, but keep maskRequested
|
||||
temp.typeRequested = temp.typeBase;
|
||||
}
|
||||
|
||||
{
|
||||
PVStructurePtr proto(temp.typeRequested->build());
|
||||
|
||||
// base -> request may be sparce mapping
|
||||
temp.base2req.resize(base.getNextFieldOffset());
|
||||
// request -> base is dense mapping
|
||||
temp.req2base.resize(proto->getNextFieldOffset());
|
||||
|
||||
// special handling for whole structure mapping. in part because getSubField(0) isn't allowed
|
||||
temp.base2req[0] = Mapping(0, false);
|
||||
temp.req2base[0] = Mapping(0, false);
|
||||
|
||||
// Iterate prototype of requested to map with base field offsets.
|
||||
// which is handled as a special case below.
|
||||
// We also don't try to prevent redundant copies if both leaf and compress bits are set.
|
||||
for(size_t r=1, N=proto->getNextFieldOffset(); r<N; r++) {
|
||||
PVField::const_shared_pointer fld_req(proto->getSubFieldT(r)),
|
||||
fld_base(base.getSubFieldT(fld_req->getFullName()));
|
||||
const size_t b = fld_base->getFieldOffset();
|
||||
|
||||
if(!temp.requestedMask().get(b))
|
||||
continue;
|
||||
|
||||
bool leaf = fld_base->getField()->getType()!=structure;
|
||||
|
||||
// initialize mapping when our bit is set
|
||||
temp.base2req[b] = Mapping(r, leaf);
|
||||
temp.req2base[r] = Mapping(b, leaf);
|
||||
|
||||
// add ourself to all "compress" bit mappings of enclosing structures
|
||||
for(const PVStructure *parent = fld_req->getParent(); parent; parent = parent->getParent()) {
|
||||
temp.req2base[parent->getFieldOffset()].tomask .set(b);
|
||||
temp.req2base[parent->getFieldOffset()].frommask.set(r);
|
||||
}
|
||||
|
||||
for(const PVStructure *parent = fld_base->getParent(); parent; parent = parent->getParent()) {
|
||||
temp.base2req[parent->getFieldOffset()].tomask .set(r);
|
||||
temp.base2req[parent->getFieldOffset()].frommask.set(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
temp.maskRequested.set(0);
|
||||
|
||||
if(temp.maskRequested.nextSetBit(1)==-1) {
|
||||
ok = false;
|
||||
temp.messages+="Empty field selection";
|
||||
}
|
||||
|
||||
if(!ok)
|
||||
throw std::runtime_error(temp.messages);
|
||||
|
||||
swap(temp);
|
||||
}
|
||||
|
||||
bool PVRequestMapper::_compute(const PVStructure& base, const PVStructure& pvReq,
|
||||
FieldBuilderPtr& builder, bool keepids, unsigned depth)
|
||||
{
|
||||
bool ok = true;
|
||||
const StringArray& reqNames = pvReq.getStructure()->getFieldNames();
|
||||
|
||||
for(size_t i=0, N=reqNames.size(); i<N; i++) {
|
||||
// iterate through requested fields
|
||||
|
||||
PVField::const_shared_pointer subtype(base.getSubField(reqNames[i]));
|
||||
const FieldConstPtr& subReq = pvReq.getStructure()->getFields()[i];
|
||||
|
||||
if(subReq->getType()!=structure) {
|
||||
// pvRequest .field was not properly composed
|
||||
std::ostringstream msg;
|
||||
// not a great warning message as it doesn't distinguish 'a.value' from 'b.value',
|
||||
// but getFullName() whould prefix with 'field.', which would probably cause
|
||||
// more frequent confusion...
|
||||
msg<<"request invalid '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
|
||||
messages+=msg.str();
|
||||
ok = false;
|
||||
|
||||
} else if(!subtype) {
|
||||
// requested field does not actually exist in base
|
||||
std::ostringstream msg;
|
||||
msg<<"No field '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
|
||||
messages+=msg.str();
|
||||
|
||||
} else if(depth>=maxDepth // exceeds max recursion depth
|
||||
|| subtype->getField()->getType()!=structure // requested field is a leaf
|
||||
|| static_cast<const Structure&>(*subReq).getFieldNames().empty() // requests all sub-fields
|
||||
)
|
||||
{
|
||||
// just add the whole thing
|
||||
builder = builder->add(reqNames[i], subtype->getField());
|
||||
for(size_t j=subtype->getFieldOffset(), N=subtype->getNextFieldOffset(); j<N; j++)
|
||||
maskRequested.set(j);
|
||||
|
||||
if(subtype->getField()->getType()!=structure
|
||||
&& !static_cast<const Structure&>(*subReq).getFieldNames().empty())
|
||||
{
|
||||
// attempt to select below a leaf field
|
||||
std::ostringstream msg;
|
||||
msg<<"Leaf field '"<<pvReq.getFullName()<<"' ";
|
||||
messages+=msg.str();
|
||||
} else if(depth>=maxDepth) {
|
||||
std::ostringstream msg;
|
||||
msg<<"selection truncated at '"<<pvReq.getFullName()<<"' ";
|
||||
messages+=msg.str();
|
||||
}
|
||||
|
||||
} else {
|
||||
// recurse into sub-structure
|
||||
const PVStructure& substruct = static_cast<const PVStructure&>(*subtype);
|
||||
|
||||
builder = builder->addNestedStructure(reqNames[i]);
|
||||
maskRequested.set(substruct.getFieldOffset());
|
||||
|
||||
if(keepids)
|
||||
builder = builder->setId(substruct.getStructure()->getID());
|
||||
|
||||
_compute(substruct,
|
||||
static_cast<const PVStructure&>(*pvReq.getPVFields()[i]),
|
||||
builder, keepids, depth+1u);
|
||||
|
||||
builder = builder->endNested();
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void PVRequestMapper::copyBaseToRequested(
|
||||
const PVStructure& base,
|
||||
const BitSet& baseMask,
|
||||
PVStructure& request,
|
||||
BitSet& requestMask
|
||||
) const {
|
||||
assert(base.getStructure()==typeBase);
|
||||
assert(request.getStructure()==typeRequested);
|
||||
_map(base, baseMask, request, requestMask, false);
|
||||
}
|
||||
|
||||
void PVRequestMapper::copyBaseFromRequested(
|
||||
PVStructure& base,
|
||||
BitSet& baseMask,
|
||||
const PVStructure& request,
|
||||
const BitSet& requestMask
|
||||
) const {
|
||||
assert(base.getStructure()==typeBase);
|
||||
assert(request.getStructure()==typeRequested);
|
||||
_map(request, requestMask, base, baseMask, true);
|
||||
}
|
||||
|
||||
void PVRequestMapper::_map(const PVStructure& src, const BitSet& maskSrc,
|
||||
PVStructure& dest, BitSet& maskDest,
|
||||
bool dir_r2b) const
|
||||
{
|
||||
{
|
||||
scratch = maskSrc;
|
||||
const mapping_t& map = dir_r2b ? req2base : base2req;
|
||||
|
||||
assert(map.size()==src.getNumberFields());
|
||||
|
||||
for(int32 i=scratch.nextSetBit(0), N=map.size(); i>=0 && i<N; i=scratch.nextSetBit(i+1)) {
|
||||
const Mapping& M = map[i];
|
||||
if(!M.valid) {
|
||||
assert(!dir_r2b); // only base -> requested mapping can have holes
|
||||
|
||||
} else if(M.leaf) {
|
||||
// just copy
|
||||
dest.getSubFieldT(M.to)->copy(*src.getSubFieldT(i));
|
||||
maskDest.set(M.to);
|
||||
|
||||
} else {
|
||||
// set bits of all sub-fields (in requested structure)
|
||||
// these indicies are always >i
|
||||
scratch |= M.frommask;
|
||||
|
||||
// we will also set the individual bits, but if a compress bit is set in the input,
|
||||
// then set the corresponding bit in the output.
|
||||
maskDest.set(M.to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVRequestMapper::_mapMask(const BitSet& maskSrc,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const
|
||||
{
|
||||
if(maskSrc.isEmpty()) {
|
||||
// no-op
|
||||
|
||||
} else {
|
||||
const mapping_t& map = dir_r2b ? req2base : base2req;
|
||||
|
||||
for(int32 i=maskSrc.nextSetBit(0), N=map.size(); i>=0 && i<N; i=maskSrc.nextSetBit(i+1)) {
|
||||
const Mapping& M = map[i];
|
||||
if(!M.valid) {
|
||||
assert(!dir_r2b); // only base -> requested mapping can have holes
|
||||
|
||||
} else {
|
||||
maskDest.set(M.to);
|
||||
|
||||
if(!M.leaf) {
|
||||
maskDest |= M.tomask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PVRequestMapper::swap(PVRequestMapper& other)
|
||||
{
|
||||
typeBase.swap(other.typeBase);
|
||||
typeRequested.swap(other.typeRequested);
|
||||
maskRequested.swap(other.maskRequested);
|
||||
base2req.swap(other.base2req);
|
||||
req2base.swap(other.req2base);
|
||||
messages.swap(other.messages);
|
||||
scratch.swap(other.scratch); // paranoia
|
||||
}
|
||||
|
||||
void PVRequestMapper::reset()
|
||||
{
|
||||
typeBase.reset();
|
||||
typeRequested.reset();
|
||||
maskRequested.clear();
|
||||
base2req.clear();
|
||||
req2base.clear();
|
||||
messages.clear();
|
||||
scratch.clear(); // paranoia
|
||||
}
|
||||
|
||||
}} //namespace epics::pvData
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mes
|
||||
@@ -23,7 +24,7 @@ namespace epics { namespace pvData {
|
||||
* 1) same instance
|
||||
* 2) same type (field and scalar/element), same name, same subfields (if any)
|
||||
*/
|
||||
bool compare(const Field& a, const Field& b)
|
||||
bool operator==(const Field& a, const Field& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -33,53 +34,53 @@ bool compare(const Field& a, const Field& b)
|
||||
case scalar: {
|
||||
const Scalar &A=static_cast<const Scalar&>(a);
|
||||
const Scalar &B=static_cast<const Scalar&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
case scalarArray: {
|
||||
const ScalarArray &A=static_cast<const ScalarArray&>(a);
|
||||
const ScalarArray &B=static_cast<const ScalarArray&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
case structure: {
|
||||
const Structure &A=static_cast<const Structure&>(a);
|
||||
const Structure &B=static_cast<const Structure&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
case structureArray: {
|
||||
const StructureArray &A=static_cast<const StructureArray&>(a);
|
||||
const StructureArray &B=static_cast<const StructureArray&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
case union_: {
|
||||
const Union &A=static_cast<const Union&>(a);
|
||||
const Union &B=static_cast<const Union&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
case unionArray: {
|
||||
const UnionArray &A=static_cast<const UnionArray&>(a);
|
||||
const UnionArray &B=static_cast<const UnionArray&>(b);
|
||||
return compare(A, B);
|
||||
return A==B;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("Invalid Field type in comparison");
|
||||
}
|
||||
}
|
||||
|
||||
bool compare(const Scalar& a, const Scalar& b)
|
||||
bool operator==(const Scalar& a, const Scalar& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getScalarType()==b.getScalarType();
|
||||
}
|
||||
|
||||
bool compare(const ScalarArray& a, const ScalarArray& b)
|
||||
bool operator==(const ScalarArray& a, const ScalarArray& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getElementType()==b.getElementType();
|
||||
}
|
||||
|
||||
bool compare(const Structure& a, const Structure& b)
|
||||
bool operator==(const Structure& a, const Structure& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -101,12 +102,12 @@ bool compare(const Structure& a, const Structure& b)
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool compare(const StructureArray& a, const StructureArray& b)
|
||||
bool operator==(const StructureArray& a, const StructureArray& b)
|
||||
{
|
||||
return *(a.getStructure().get())==*(b.getStructure().get());
|
||||
}
|
||||
|
||||
bool compare(const Union& a, const Union& b)
|
||||
bool operator==(const Union& a, const Union& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -128,12 +129,12 @@ bool compare(const Union& a, const Union& b)
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool compare(const UnionArray& a, const UnionArray& b)
|
||||
bool operator==(const UnionArray& a, const UnionArray& b)
|
||||
{
|
||||
return *(a.getUnion().get())==*(b.getUnion().get());
|
||||
}
|
||||
|
||||
bool compare(const BoundedString& a, const BoundedString& b)
|
||||
bool operator==(const BoundedString& a, const BoundedString& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -271,7 +272,7 @@ bool compareField(const PVUnion* left, const PVUnion* right)
|
||||
|
||||
if (ls->isVariant())
|
||||
{
|
||||
const PVField::const_shared_pointer& lval = left->get();
|
||||
PVFieldPtr lval = left->get();
|
||||
if (lval.get() == 0)
|
||||
return right->get().get() == 0;
|
||||
else
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* Convert.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -12,8 +13,6 @@
|
||||
#include <stdexcept>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
/*FieldCreateFactory.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
@@ -14,18 +19,11 @@
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsString.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/reftrack.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
#include <pv/thread.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::size_t;
|
||||
@@ -33,79 +31,16 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
size_t Field::num_instances;
|
||||
|
||||
|
||||
struct Field::Helper {
|
||||
static unsigned hash(Field *fld) {
|
||||
std::ostringstream key;
|
||||
// hash the output of operator<<()
|
||||
// not efficient, but stable within this process.
|
||||
key<<(*fld);
|
||||
unsigned H = epicsStrHash(key.str().c_str(), 0xbadc0de1);
|
||||
fld->m_hash = H;
|
||||
return H;
|
||||
}
|
||||
};
|
||||
|
||||
struct FieldCreate::Helper {
|
||||
template<typename FLD>
|
||||
static void cache(const FieldCreate *create, std::tr1::shared_ptr<FLD>& ent) {
|
||||
unsigned hash = Field::Helper::hash(ent.get());
|
||||
|
||||
Lock G(create->mutex);
|
||||
// we examine raw pointers stored in create->cache, which is safe under create->mutex
|
||||
|
||||
std::pair<cache_t::iterator, cache_t::iterator> itp(create->cache.equal_range(hash));
|
||||
for(; itp.first!=itp.second; ++itp.first) {
|
||||
Field* cent(itp.first->second);
|
||||
FLD* centx(dynamic_cast<FLD*>(cent));
|
||||
if(centx && compare(*centx, *ent)) {
|
||||
try{
|
||||
ent = std::tr1::static_pointer_cast<FLD>(cent->shared_from_this());
|
||||
return;
|
||||
}catch(std::tr1::bad_weak_ptr&){
|
||||
// we're racing destruction.
|
||||
// add a new entry.
|
||||
// Field::~Field is in the process of removing this old one.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create->cache.insert(std::make_pair(hash, ent.get()));
|
||||
// cache cleaned from Field::~Field
|
||||
}
|
||||
};
|
||||
static DebugLevel debugLevel = lowDebug;
|
||||
|
||||
Field::Field(Type type)
|
||||
: m_fieldType(type)
|
||||
, m_hash(0)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
}
|
||||
|
||||
Field::~Field() {
|
||||
REFTRACE_DECREMENT(num_instances);
|
||||
FieldCreatePtr create(getFieldCreate());
|
||||
|
||||
Lock G(create->mutex);
|
||||
|
||||
std::pair<FieldCreate::cache_t::iterator, FieldCreate::cache_t::iterator> itp(create->cache.equal_range(m_hash));
|
||||
for(; itp.first!=itp.second; ++itp.first) {
|
||||
Field* cent(itp.first->second);
|
||||
if(cent==this) {
|
||||
create->cache.erase(itp.first);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVField> Field::build() const
|
||||
{
|
||||
FieldConstPtr self(shared_from_this());
|
||||
return getPVDataCreate()->createPVField(self);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const Field& f)
|
||||
{
|
||||
@@ -176,11 +111,6 @@ void Scalar::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*contro
|
||||
}
|
||||
|
||||
|
||||
std::tr1::shared_ptr<PVScalar> Scalar::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVScalar(std::tr1::static_pointer_cast<const Scalar>(shared_from_this()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string BoundedString::getID() const
|
||||
@@ -347,11 +277,6 @@ void ScalarArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*c
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVScalarArray> ScalarArray::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVScalarArray(std::tr1::static_pointer_cast<const ScalarArray>(shared_from_this()));
|
||||
}
|
||||
|
||||
|
||||
BoundedScalarArray::~BoundedScalarArray() {}
|
||||
|
||||
@@ -403,7 +328,9 @@ StructureArray::StructureArray(StructureConstPtr const & structure)
|
||||
{
|
||||
}
|
||||
|
||||
StructureArray::~StructureArray() {}
|
||||
StructureArray::~StructureArray() {
|
||||
if(debugLevel==highDebug) printf("~StructureArray\n");
|
||||
}
|
||||
|
||||
string StructureArray::getID() const
|
||||
{
|
||||
@@ -430,17 +357,14 @@ void StructureArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl*
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVStructure> > > StructureArray::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVStructureArray(std::tr1::static_pointer_cast<const StructureArray>(shared_from_this()));
|
||||
}
|
||||
|
||||
UnionArray::UnionArray(UnionConstPtr const & _punion)
|
||||
: Array(unionArray),punion(_punion)
|
||||
{
|
||||
}
|
||||
|
||||
UnionArray::~UnionArray() {}
|
||||
UnionArray::~UnionArray() {
|
||||
if(debugLevel==highDebug) printf("~UnionArray\n");
|
||||
}
|
||||
|
||||
string UnionArray::getID() const
|
||||
{
|
||||
@@ -475,11 +399,6 @@ void UnionArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*co
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVUnion> > > UnionArray::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVUnionArray(std::tr1::static_pointer_cast<const UnionArray>(shared_from_this()));
|
||||
}
|
||||
|
||||
const string Structure::DEFAULT_ID = Structure::defaultId();
|
||||
|
||||
const string & Structure::defaultId()
|
||||
@@ -533,10 +452,11 @@ string Structure::getID() const
|
||||
}
|
||||
|
||||
FieldConstPtr Structure::getField(string const & fieldName) const {
|
||||
for(size_t i=0, N=fields.size(); i<N; i++) {
|
||||
if(fieldName==fieldNames[i]) {
|
||||
return fields[i];
|
||||
}
|
||||
size_t numberFields = fields.size();
|
||||
for(size_t i=0; i<numberFields; i++) {
|
||||
FieldConstPtr pfield = fields[i];
|
||||
int result = fieldName.compare(fieldNames[i]);
|
||||
if(result==0) return pfield;
|
||||
}
|
||||
return FieldConstPtr();
|
||||
}
|
||||
@@ -617,11 +537,6 @@ void Structure::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*con
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVStructure> Structure::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVStructure(std::tr1::static_pointer_cast<const Structure>(shared_from_this()));
|
||||
}
|
||||
|
||||
const string Union::DEFAULT_ID = Union::defaultId();
|
||||
|
||||
const string & Union::defaultId()
|
||||
@@ -689,41 +604,6 @@ Union::Union (
|
||||
|
||||
Union::~Union() { }
|
||||
|
||||
int32 Union::guess(Type t, ScalarType s) const
|
||||
{
|
||||
if(t!=scalar && t!=scalarArray)
|
||||
THROW_EXCEPTION2(std::logic_error, "PVUnion::guess() only support scalar and scalarArray");
|
||||
|
||||
int32 ret = -1;
|
||||
for(size_t i=0, N=fields.size(); i<N; i++)
|
||||
{
|
||||
if(fields[i]->getType()!=t)
|
||||
continue;
|
||||
|
||||
ScalarType type;
|
||||
switch(fields[i]->getType()) {
|
||||
case scalar:
|
||||
type = static_cast<const Scalar*>(fields[i].get())->getScalarType();
|
||||
break;
|
||||
case scalarArray:
|
||||
type = static_cast<const ScalarArray*>(fields[i].get())->getElementType();
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if(type==s) {
|
||||
// exact match
|
||||
ret = i;
|
||||
break; // we're done
|
||||
|
||||
} else if(ret==-1) {
|
||||
// first partial match
|
||||
ret = i;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
string Union::getID() const
|
||||
{
|
||||
@@ -820,102 +700,18 @@ void Union::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVUnion> Union::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVUnion(std::tr1::static_pointer_cast<const Union>(shared_from_this()));
|
||||
}
|
||||
|
||||
FieldBuilder::FieldBuilder()
|
||||
:fieldCreate(getFieldCreate())
|
||||
,idSet(false)
|
||||
,nestedClassToBuild(structure)
|
||||
,nestedArray(false)
|
||||
,createNested(true)
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(const Structure* S)
|
||||
:fieldCreate(getFieldCreate())
|
||||
,id(S->getID())
|
||||
,idSet(!id.empty())
|
||||
,fieldNames(S->getFieldNames())
|
||||
,fields(S->getFields())
|
||||
,parentBuilder()
|
||||
,nestedClassToBuild(structure)
|
||||
,nestedName()
|
||||
,nestedArray(false)
|
||||
,createNested(false)
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
|
||||
const std::string& name,
|
||||
const Structure* S)
|
||||
:fieldCreate(_parentBuilder->fieldCreate)
|
||||
,id(S->getID())
|
||||
,idSet(!id.empty())
|
||||
,fieldNames(S->getFieldNames())
|
||||
,fields(S->getFields())
|
||||
,parentBuilder(_parentBuilder)
|
||||
,nestedClassToBuild(structure)
|
||||
,nestedName(name)
|
||||
,nestedArray(false)
|
||||
,createNested(false)
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
|
||||
const std::string& name,
|
||||
const StructureArray* S)
|
||||
:fieldCreate(getFieldCreate())
|
||||
,id(S->getStructure()->getID())
|
||||
,idSet(!id.empty())
|
||||
,fieldNames(S->getStructure()->getFieldNames())
|
||||
,fields(S->getStructure()->getFields())
|
||||
,parentBuilder(_parentBuilder)
|
||||
,nestedClassToBuild(structure)
|
||||
,nestedName(name)
|
||||
,nestedArray(true)
|
||||
,createNested(false)
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
|
||||
const std::string& name,
|
||||
const Union* S)
|
||||
:fieldCreate(getFieldCreate())
|
||||
,id(S->getID())
|
||||
,idSet(!id.empty())
|
||||
,fieldNames(S->getFieldNames())
|
||||
,fields(S->getFields())
|
||||
,parentBuilder(_parentBuilder)
|
||||
,nestedClassToBuild(union_)
|
||||
,nestedName(name)
|
||||
,nestedArray(false)
|
||||
,createNested(false)
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
|
||||
const std::string& name,
|
||||
const UnionArray* S)
|
||||
:fieldCreate(getFieldCreate())
|
||||
,id(S->getUnion()->getID())
|
||||
,idSet(!id.empty())
|
||||
,fieldNames(S->getUnion()->getFieldNames())
|
||||
,fields(S->getUnion()->getFields())
|
||||
,parentBuilder(_parentBuilder)
|
||||
,nestedClassToBuild(union_)
|
||||
,nestedName(name)
|
||||
,nestedArray(true)
|
||||
,createNested(false)
|
||||
{}
|
||||
FieldBuilder::FieldBuilder() : fieldCreate(getFieldCreate()), idSet(false) {}
|
||||
|
||||
FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder,
|
||||
string const & _nestedName,
|
||||
Type _nestedClassToBuild, bool _nestedArray)
|
||||
:fieldCreate(_parentBuilder->fieldCreate)
|
||||
,idSet(false)
|
||||
,parentBuilder(_parentBuilder)
|
||||
,nestedClassToBuild(_nestedClassToBuild)
|
||||
,nestedName(_nestedName)
|
||||
,nestedArray(_nestedArray)
|
||||
,createNested(true)
|
||||
Type _nestedClassToBuild, bool _nestedArray) :
|
||||
fieldCreate(getFieldCreate()),
|
||||
idSet(false),
|
||||
parentBuilder(_parentBuilder),
|
||||
nestedClassToBuild(_nestedClassToBuild),
|
||||
nestedName(_nestedName),
|
||||
nestedArray(_nestedArray)
|
||||
{}
|
||||
|
||||
void FieldBuilder::reset()
|
||||
@@ -926,19 +722,6 @@ void FieldBuilder::reset()
|
||||
fields.clear();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::begin()
|
||||
{
|
||||
FieldBuilderPtr ret(new FieldBuilder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::begin(StructureConstPtr S)
|
||||
{
|
||||
FieldBuilderPtr ret(new FieldBuilder(S.get()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
FieldBuilderPtr FieldBuilder::setId(string const & id)
|
||||
{
|
||||
this->id = id;
|
||||
@@ -948,57 +731,56 @@ FieldBuilderPtr FieldBuilder::setId(string const & id)
|
||||
|
||||
FieldBuilderPtr FieldBuilder::add(string const & name, ScalarType scalarType)
|
||||
{
|
||||
return add(name, fieldCreate->createScalar(scalarType));
|
||||
fields.push_back(fieldCreate->createScalar(scalarType)); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addBoundedString(std::string const & name, std::size_t maxLength)
|
||||
{
|
||||
return add(name, fieldCreate->createBoundedString(maxLength));
|
||||
fields.push_back(fieldCreate->createBoundedString(maxLength)); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::add(string const & name, FieldConstPtr const & field)
|
||||
{
|
||||
const Field* cur = findField(name, field->getType());
|
||||
if(!cur) {
|
||||
fields.push_back(field); fieldNames.push_back(name);
|
||||
} else if(*cur!=*field) {
|
||||
THROW_EXCEPTION2(std::runtime_error, "duplicate field name w/ different type : "+name);
|
||||
} // else exact duplicate is silently ignored
|
||||
fields.push_back(field); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addArray(string const & name, ScalarType scalarType)
|
||||
{
|
||||
return add(name, fieldCreate->createScalarArray(scalarType));
|
||||
fields.push_back(fieldCreate->createScalarArray(scalarType)); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addFixedArray(string const & name, ScalarType scalarType, size_t size)
|
||||
{
|
||||
return add(name, fieldCreate->createFixedScalarArray(scalarType, size));
|
||||
fields.push_back(fieldCreate->createFixedScalarArray(scalarType, size)); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addBoundedArray(string const & name, ScalarType scalarType, size_t size)
|
||||
{
|
||||
return add(name, fieldCreate->createBoundedScalarArray(scalarType, size));
|
||||
fields.push_back(fieldCreate->createBoundedScalarArray(scalarType, size)); fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const & element)
|
||||
{
|
||||
FieldConstPtr fld;
|
||||
switch (element->getType())
|
||||
{
|
||||
case structure:
|
||||
fld = fieldCreate->createStructureArray(static_pointer_cast<const Structure>(element));
|
||||
fields.push_back(fieldCreate->createStructureArray(static_pointer_cast<const Structure>(element)));
|
||||
break;
|
||||
case union_:
|
||||
fld = fieldCreate->createUnionArray(static_pointer_cast<const Union>(element));
|
||||
fields.push_back(fieldCreate->createUnionArray(static_pointer_cast<const Union>(element)));
|
||||
break;
|
||||
case scalar:
|
||||
|
||||
if (std::tr1::dynamic_pointer_cast<const BoundedString>(element).get())
|
||||
THROW_EXCEPTION2(std::invalid_argument, "bounded string arrays are not supported");
|
||||
|
||||
fld = fieldCreate->createScalarArray(static_pointer_cast<const Scalar>(element)->getScalarType());
|
||||
fields.push_back(fieldCreate->createScalarArray(static_pointer_cast<const Scalar>(element)->getScalarType()));
|
||||
break;
|
||||
// scalarArray?
|
||||
default:
|
||||
@@ -1007,7 +789,8 @@ FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const
|
||||
THROW_EXCEPTION2(std::invalid_argument, msg.str());
|
||||
}
|
||||
|
||||
return add(name, fld);
|
||||
fieldNames.push_back(name);
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldConstPtr FieldBuilder::createFieldInternal(Type type)
|
||||
@@ -1057,111 +840,48 @@ UnionConstPtr FieldBuilder::createUnion()
|
||||
return field;
|
||||
}
|
||||
|
||||
const Field* FieldBuilder::findField(const std::string& name, Type ftype)
|
||||
{
|
||||
// linear search on the theory that the number of fields is small
|
||||
for(size_t i=0; i<fieldNames.size(); i++)
|
||||
{
|
||||
if(name!=fieldNames[i])
|
||||
continue;
|
||||
|
||||
if(fields[i]->getType()!=ftype)
|
||||
THROW_EXCEPTION2(std::invalid_argument, "nested field not required type: "+name);
|
||||
|
||||
return fields[i].get();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addNestedStructure(string const & name)
|
||||
{
|
||||
const Field *cur = findField(name, structure);
|
||||
if(cur) {
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
|
||||
static_cast<const Structure*>(cur)));
|
||||
}
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, false));
|
||||
}
|
||||
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addNestedUnion(string const & name)
|
||||
{
|
||||
const Field *cur = findField(name, union_);
|
||||
if(cur) {
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
|
||||
static_cast<const Union*>(cur)));
|
||||
}
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, false));
|
||||
}
|
||||
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addNestedStructureArray(string const & name)
|
||||
{
|
||||
const Field *cur = findField(name, structureArray);
|
||||
if(cur) {
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
|
||||
static_cast<const StructureArray*>(cur)));
|
||||
}
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, true));
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addNestedUnionArray(string const & name)
|
||||
{
|
||||
const Field *cur = findField(name, unionArray);
|
||||
if(cur) {
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
|
||||
static_cast<const UnionArray*>(cur)));
|
||||
}
|
||||
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, true));
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::endNested()
|
||||
{
|
||||
if (!parentBuilder)
|
||||
THROW_EXCEPTION2(std::runtime_error, "FieldBuilder::endNested() can only be called to create nested fields");
|
||||
if (!parentBuilder.get())
|
||||
THROW_EXCEPTION2(std::runtime_error, "this method can only be called to create nested fields");
|
||||
|
||||
FieldConstPtr nestedField = createFieldInternal(nestedClassToBuild);
|
||||
|
||||
if(createNested) {
|
||||
if (nestedArray)
|
||||
parentBuilder->addArray(nestedName, nestedField);
|
||||
else
|
||||
parentBuilder->add(nestedName, nestedField);
|
||||
return parentBuilder;
|
||||
} else {
|
||||
for(size_t i=0, N = parentBuilder->fieldNames.size(); i<N; i++)
|
||||
{
|
||||
if(nestedName!=parentBuilder->fieldNames[i])
|
||||
continue;
|
||||
|
||||
if(nestedArray) {
|
||||
if(nestedClassToBuild==structure)
|
||||
parentBuilder->fields[i] = fieldCreate->createStructureArray(std::tr1::static_pointer_cast<const Structure>(nestedField));
|
||||
else if(nestedClassToBuild==union_)
|
||||
parentBuilder->fields[i] = fieldCreate->createUnionArray(std::tr1::static_pointer_cast<const Union>(nestedField));
|
||||
else
|
||||
throw std::logic_error("bad nested class");
|
||||
} else {
|
||||
parentBuilder->fields[i] = nestedField;
|
||||
}
|
||||
return parentBuilder;
|
||||
}
|
||||
// this only reached if bug in ctor
|
||||
THROW_EXCEPTION2(std::logic_error, "no nested field field?");
|
||||
}
|
||||
if (nestedArray)
|
||||
parentBuilder->addArray(nestedName, nestedField);
|
||||
else
|
||||
parentBuilder->add(nestedName, nestedField);
|
||||
|
||||
return parentBuilder;
|
||||
}
|
||||
|
||||
|
||||
FieldBuilderPtr FieldCreate::createFieldBuilder() const
|
||||
{
|
||||
return FieldBuilderPtr(new FieldBuilder());
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldCreate::createFieldBuilder(StructureConstPtr S) const
|
||||
{
|
||||
FieldBuilderPtr ret(new FieldBuilder(S.get()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
|
||||
{
|
||||
if(scalarType<0 || scalarType>MAX_SCALAR_TYPE) {
|
||||
@@ -1175,9 +895,10 @@ ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
|
||||
|
||||
BoundedStringConstPtr FieldCreate::createBoundedString(std::size_t maxLength) const
|
||||
{
|
||||
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength), Field::Deleter());
|
||||
BoundedStringConstPtr sa = s;
|
||||
return sa;
|
||||
}
|
||||
|
||||
ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const
|
||||
@@ -1199,9 +920,10 @@ ScalarArrayConstPtr FieldCreate::createFixedScalarArray(ScalarType elementType,
|
||||
THROW_EXCEPTION2(std::invalid_argument, strm.str());
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size), Field::Deleter());
|
||||
ScalarArrayConstPtr sa = s;
|
||||
return sa;
|
||||
}
|
||||
|
||||
ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType, size_t size) const
|
||||
@@ -1212,9 +934,10 @@ ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType
|
||||
THROW_EXCEPTION2(std::invalid_argument, strm.str());
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size), Field::Deleter());
|
||||
ScalarArrayConstPtr sa = s;
|
||||
return sa;
|
||||
}
|
||||
|
||||
StructureConstPtr FieldCreate::createStructure () const
|
||||
@@ -1224,56 +947,13 @@ StructureConstPtr FieldCreate::createStructure () const
|
||||
return createStructure(fieldNames,fields);
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool xisalnum(char c)
|
||||
{
|
||||
return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9');
|
||||
}
|
||||
|
||||
void validateFieldName(const std::string& n)
|
||||
{
|
||||
// enforce [A-Za-z_][A-Za-z0-9_]*
|
||||
if(n.size()==0)
|
||||
throw std::invalid_argument("zero length field names not allowed");
|
||||
if(n[0]>='0' && n[0]<='9') {
|
||||
std::ostringstream msg;
|
||||
msg<<"Field name \""<<n<<"\" must begin with A-Z, a-z, or '_'";
|
||||
throw std::invalid_argument(msg.str());
|
||||
}
|
||||
for(size_t i=0, N=n.size(); i<N; i++)
|
||||
{
|
||||
char c = n[i];
|
||||
if(xisalnum(c)) {}
|
||||
else {
|
||||
switch(c){
|
||||
case '_':
|
||||
break;
|
||||
default:
|
||||
{
|
||||
std::ostringstream msg;
|
||||
msg<<"Invalid charactor '"<<c<<"' ("<<(int)c<<") in field name \""<<n<<"\" "
|
||||
"must be A-Z, a-z, 0-9, or '_'";
|
||||
throw std::invalid_argument(msg.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void validateFieldNames(const StringArray& l)
|
||||
{
|
||||
for(StringArray::const_iterator it=l.begin(), end=l.end(); it!=end; ++it)
|
||||
validateFieldName(*it);
|
||||
}
|
||||
}
|
||||
|
||||
StructureConstPtr FieldCreate::createStructure (
|
||||
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields), Field::Deleter());
|
||||
StructureConstPtr structure = sp;
|
||||
return structure;
|
||||
}
|
||||
|
||||
StructureConstPtr FieldCreate::createStructure (
|
||||
@@ -1281,27 +961,28 @@ StructureConstPtr FieldCreate::createStructure (
|
||||
StringArray const & fieldNames,
|
||||
FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id), Field::Deleter());
|
||||
StructureConstPtr structure = sp;
|
||||
return structure;
|
||||
}
|
||||
|
||||
StructureArrayConstPtr FieldCreate::createStructureArray(
|
||||
StructureConstPtr const & structure) const
|
||||
{
|
||||
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure), Field::Deleter());
|
||||
StructureArrayConstPtr structureArray = sp;
|
||||
return structureArray;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createUnion (
|
||||
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields), Field::Deleter());
|
||||
UnionConstPtr punion = sp;
|
||||
return punion;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createUnion (
|
||||
@@ -1309,10 +990,10 @@ UnionConstPtr FieldCreate::createUnion (
|
||||
StringArray const & fieldNames,
|
||||
FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id), Field::Deleter());
|
||||
UnionConstPtr punion = sp;
|
||||
return punion;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createVariantUnion () const
|
||||
@@ -1323,9 +1004,10 @@ UnionConstPtr FieldCreate::createVariantUnion () const
|
||||
UnionArrayConstPtr FieldCreate::createUnionArray(
|
||||
UnionConstPtr const & punion) const
|
||||
{
|
||||
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion), Field::Deleter());
|
||||
UnionArrayConstPtr unionArray = sp;
|
||||
return unionArray;
|
||||
}
|
||||
|
||||
UnionArrayConstPtr FieldCreate::createVariantUnionArray () const
|
||||
@@ -1357,7 +1039,6 @@ StructureConstPtr FieldCreate::appendFields(
|
||||
StringArray const & fieldNames,
|
||||
FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
StringArray const & oldNames = structure->getFieldNames();
|
||||
FieldConstPtrArray const & oldFields = structure->getFields();
|
||||
size_t oldLen = oldNames.size();
|
||||
@@ -1454,10 +1135,12 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
|
||||
size_t size = SerializeHelper::readSize(buffer, control);
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(
|
||||
new BoundedString(size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
new BoundedString(size),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("invalid type encoding");
|
||||
@@ -1482,17 +1165,19 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(
|
||||
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(
|
||||
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
else if (typeCode == 0x80)
|
||||
@@ -1504,9 +1189,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
// Type type = Type.structureArray;
|
||||
StructureConstPtr elementStructure = std::tr1::static_pointer_cast<const Structure>(control->cachedDeserialize(buffer));
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure), Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
}
|
||||
else if (typeCode == 0x81)
|
||||
{
|
||||
@@ -1517,9 +1202,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
// Type type = Type.unionArray;
|
||||
UnionConstPtr elementUnion = std::tr1::static_pointer_cast<const Union>(control->cachedDeserialize(buffer));
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion), Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
}
|
||||
else if (typeCode == 0x82)
|
||||
{
|
||||
@@ -1535,58 +1220,46 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
struct field_factory {
|
||||
FieldCreatePtr fieldCreate;
|
||||
field_factory() :fieldCreate(new FieldCreate()) {
|
||||
registerRefCounter("Field", &Field::num_instances);
|
||||
registerRefCounter("Thread", &Thread::num_instances);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static detail::field_factory* field_factory_s;
|
||||
static epicsThreadOnceId field_factory_once = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static void field_factory_init(void*)
|
||||
// TODO replace with non-locking singleton pattern
|
||||
FieldCreatePtr FieldCreate::getFieldCreate()
|
||||
{
|
||||
try {
|
||||
field_factory_s = new detail::field_factory;
|
||||
}catch(std::exception& e){
|
||||
std::cerr<<"Error initializing getFieldCreate() : "<<e.what()<<"\n";
|
||||
}
|
||||
}
|
||||
LOCAL_STATIC_LOCK;
|
||||
static FieldCreatePtr fieldCreate;
|
||||
static Mutex mutex;
|
||||
|
||||
const FieldCreatePtr& FieldCreate::getFieldCreate()
|
||||
{
|
||||
epicsThreadOnce(&field_factory_once, &field_factory_init, 0);
|
||||
if(!field_factory_s->fieldCreate)
|
||||
throw std::logic_error("getFieldCreate() not initialized");
|
||||
return field_factory_s->fieldCreate;
|
||||
Lock xx(mutex);
|
||||
if(fieldCreate.get()==0) fieldCreate = FieldCreatePtr(new FieldCreate());
|
||||
return fieldCreate;
|
||||
}
|
||||
|
||||
FieldCreate::FieldCreate()
|
||||
{
|
||||
for (int i = 0; i <= MAX_SCALAR_TYPE; i++)
|
||||
{
|
||||
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)));
|
||||
Helper::cache(this, sp);
|
||||
scalars.push_back(sp);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)), Field::Deleter());
|
||||
ScalarConstPtr p = sp;
|
||||
scalars.push_back(p);
|
||||
|
||||
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)));
|
||||
Helper::cache(this, spa);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)), Field::Deleter());
|
||||
ScalarArrayConstPtr pa = spa;
|
||||
scalarArrays.push_back(spa);
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<Union> su(new Union());
|
||||
Helper::cache(this, su);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> su(new Union(), Field::Deleter());
|
||||
variantUnion = su;
|
||||
|
||||
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion));
|
||||
Helper::cache(this, sua);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion), Field::Deleter());
|
||||
variantUnionArray = sua;
|
||||
}
|
||||
|
||||
FieldCreatePtr getFieldCreate() {
|
||||
return FieldCreate::getFieldCreate();
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
namespace std{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
SRC_DIRS += $(PVDATA_SRC)/factory
|
||||
|
||||
INC += pv/factory.h
|
||||
INC += factory.h
|
||||
LIBSRCS += TypeFunc.cpp
|
||||
LIBSRCS += FieldCreateFactory.cpp
|
||||
LIBSRCS += PVField.cpp
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*PVArray.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
/*PVDataCreateFactory.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::size_t;
|
||||
@@ -57,111 +58,135 @@ template<> const ScalarType PVFloatArray::typeCode = pvFloat;
|
||||
template<> const ScalarType PVDoubleArray::typeCode = pvDouble;
|
||||
template<> const ScalarType PVStringArray::typeCode = pvString;
|
||||
|
||||
/** Default storage for scalar values
|
||||
*/
|
||||
template<typename T>
|
||||
class BasePVScalar : public PVScalarValue<T> {
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
|
||||
BasePVScalar(ScalarConstPtr const & scalar);
|
||||
virtual ~BasePVScalar();
|
||||
virtual T get() const ;
|
||||
virtual void put(T val);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher);
|
||||
private:
|
||||
T value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
PVScalarValue<T>::~PVScalarValue() {}
|
||||
BasePVScalar<T>::BasePVScalar(ScalarConstPtr const & scalar)
|
||||
: PVScalarValue<T>(scalar),value(0)
|
||||
{}
|
||||
//Note: '0' is a suitable default for all POD types (not string)
|
||||
|
||||
template<typename T>
|
||||
std::ostream& PVScalarValue<T>::dumpValue(std::ostream& o) const
|
||||
BasePVScalar<T>::~BasePVScalar() {}
|
||||
|
||||
template<typename T>
|
||||
T BasePVScalar<T>::get() const { return value;}
|
||||
|
||||
template<typename T>
|
||||
void BasePVScalar<T>::put(T val)
|
||||
{
|
||||
return o << get();
|
||||
value = val;
|
||||
PVField::postPut();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::operator>>=(T& value) const
|
||||
{
|
||||
value = get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::operator<<=(typename storage_t::arg_type value)
|
||||
{
|
||||
put(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::assign(const PVScalar& scalar)
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
copyUnchecked(scalar);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::copy(const PVScalar& from)
|
||||
{
|
||||
assign(from);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::copyUnchecked(const PVScalar& from)
|
||||
{
|
||||
if(this==&from)
|
||||
return;
|
||||
T result;
|
||||
from.getAs((void*)&result, typeCode);
|
||||
put(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::serialize(ByteBuffer *pbuffer,
|
||||
void BasePVScalar<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
pflusher->ensureBuffer(sizeof(T));
|
||||
pbuffer->put(storage.value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void PVScalarValue<std::string>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
SerializeHelper::serializeString(storage.value, pbuffer, pflusher);
|
||||
pbuffer->put(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::deserialize(ByteBuffer *pbuffer,
|
||||
void BasePVScalar<T>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
pflusher->ensureData(sizeof(T));
|
||||
storage.value = pbuffer->GET(T);
|
||||
value = pbuffer->GET(T);
|
||||
}
|
||||
|
||||
template<>
|
||||
void PVScalarValue<std::string>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
storage.value = SerializeHelper::deserializeString(pbuffer, pflusher);
|
||||
// TODO: check for violations of maxLength?
|
||||
}
|
||||
typedef BasePVScalar<boolean> BasePVBoolean;
|
||||
typedef BasePVScalar<int8> BasePVByte;
|
||||
typedef BasePVScalar<int16> BasePVShort;
|
||||
typedef BasePVScalar<int32> BasePVInt;
|
||||
typedef BasePVScalar<int64> BasePVLong;
|
||||
typedef BasePVScalar<uint8> BasePVUByte;
|
||||
typedef BasePVScalar<uint16> BasePVUShort;
|
||||
typedef BasePVScalar<uint32> BasePVUInt;
|
||||
typedef BasePVScalar<uint64> BasePVULong;
|
||||
typedef BasePVScalar<float> BasePVFloat;
|
||||
typedef BasePVScalar<double> BasePVDouble;
|
||||
|
||||
PVString::PVString(ScalarConstPtr const & scalar)
|
||||
: PVScalarValue<std::string>(scalar)
|
||||
// BasePVString is special case, since it implements SerializableArray
|
||||
class BasePVString : public PVString {
|
||||
public:
|
||||
typedef string value_type;
|
||||
typedef string* pointer;
|
||||
typedef const string* const_pointer;
|
||||
|
||||
BasePVString(ScalarConstPtr const & scalar);
|
||||
virtual ~BasePVString();
|
||||
virtual string get() const ;
|
||||
virtual void put(string val);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const;
|
||||
private:
|
||||
string value;
|
||||
std::size_t maxLength;
|
||||
};
|
||||
|
||||
BasePVString::BasePVString(ScalarConstPtr const & scalar)
|
||||
: PVString(scalar),value()
|
||||
{
|
||||
BoundedStringConstPtr boundedString = std::tr1::dynamic_pointer_cast<const BoundedString>(scalar);
|
||||
if (boundedString.get())
|
||||
storage.maxLength = boundedString->getMaximumLength();
|
||||
maxLength = boundedString->getMaximumLength();
|
||||
else
|
||||
storage.maxLength = 0;
|
||||
maxLength = 0;
|
||||
}
|
||||
|
||||
std::ostream& PVString::dumpValue(std::ostream& o) const
|
||||
BasePVString::~BasePVString() {}
|
||||
|
||||
string BasePVString::get() const { return value;}
|
||||
|
||||
void BasePVString::put(string val)
|
||||
{
|
||||
// we escape, but do not quote, for scalar string
|
||||
o<<escape(get());
|
||||
return o;
|
||||
if (maxLength > 0 && val.length() > maxLength)
|
||||
throw std::overflow_error("string too long");
|
||||
|
||||
value = val;
|
||||
postPut();
|
||||
}
|
||||
|
||||
/* mixing overrides (virtual functions) and overloads (different argument lists) is fun...
|
||||
* we override all overloads to avoid the "hides overloaded virtual function" warning from clang.
|
||||
* In this case we don't need/want to, so just delegate to the base class.
|
||||
*/
|
||||
void PVString::serialize(ByteBuffer *pbuffer,
|
||||
void BasePVString::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const
|
||||
{PVScalarValue<std::string>::serialize(pbuffer, pflusher);}
|
||||
{
|
||||
SerializeHelper::serializeString(value, pbuffer, pflusher);
|
||||
}
|
||||
|
||||
void PVString::serialize(ByteBuffer *pbuffer,
|
||||
void BasePVString::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
value = SerializeHelper::deserializeString(pbuffer, pflusher);
|
||||
}
|
||||
|
||||
void BasePVString::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const
|
||||
{
|
||||
// check bounds
|
||||
const size_t length = storage.value.length();
|
||||
const size_t length = /*(value == null) ? 0 :*/ value.length();
|
||||
/*if (offset < 0) offset = 0;
|
||||
else*/ if (offset > length) offset = length;
|
||||
//if (count < 0) count = length;
|
||||
@@ -171,10 +196,10 @@ void PVString::serialize(ByteBuffer *pbuffer,
|
||||
count = maxCount;
|
||||
|
||||
// write
|
||||
SerializeHelper::serializeSubstring(storage.value, offset, count, pbuffer, pflusher);
|
||||
SerializeHelper::serializeSubstring(value, offset, count, pbuffer, pflusher);
|
||||
}
|
||||
|
||||
void PVArray::checkLength(size_t len) const
|
||||
void PVArray::checkLength(size_t len)
|
||||
{
|
||||
Array::ArraySizeType type = getArray()->getArraySizeType();
|
||||
if (type != Array::variable)
|
||||
@@ -187,79 +212,61 @@ void PVArray::checkLength(size_t len) const
|
||||
}
|
||||
}
|
||||
|
||||
/** Default storage for arrays
|
||||
*/
|
||||
template<typename T>
|
||||
PVValueArray<T>::PVValueArray(ScalarArrayConstPtr const & scalarArray)
|
||||
:base_t(scalarArray)
|
||||
,value()
|
||||
class DefaultPVArray : public PVValueArray<T> {
|
||||
public:
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef std::vector<T> vector;
|
||||
typedef const std::vector<T> const_vector;
|
||||
typedef std::tr1::shared_ptr<vector> shared_vector;
|
||||
|
||||
typedef ::epics::pvData::shared_vector<T> svector;
|
||||
typedef ::epics::pvData::shared_vector<const T> const_svector;
|
||||
|
||||
DefaultPVArray(ScalarArrayConstPtr const & scalarArray);
|
||||
virtual ~DefaultPVArray();
|
||||
|
||||
virtual size_t getLength() const {return value.size();}
|
||||
virtual size_t getCapacity() const {return value.capacity();}
|
||||
|
||||
virtual void setCapacity(size_t capacity);
|
||||
virtual void setLength(size_t length);
|
||||
|
||||
virtual const_svector view() const {return value;}
|
||||
virtual void swap(const_svector &other);
|
||||
virtual void replace(const const_svector& next);
|
||||
|
||||
// from Serializable
|
||||
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const;
|
||||
private:
|
||||
const_svector value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
DefaultPVArray<T>::DefaultPVArray(ScalarArrayConstPtr const & scalarArray)
|
||||
: PVValueArray<T>(scalarArray),
|
||||
value()
|
||||
|
||||
{}
|
||||
|
||||
PVValueArray<PVStructurePtr>::PVValueArray(StructureArrayConstPtr const & structureArray)
|
||||
:base_t(structureArray)
|
||||
,structureArray(structureArray)
|
||||
|
||||
{}
|
||||
|
||||
PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
|
||||
:base_t(unionArray)
|
||||
,unionArray(unionArray)
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
PVValueArray<T>::~PVValueArray() {}
|
||||
|
||||
template<typename T>
|
||||
ArrayConstPtr PVValueArray<T>::getArray() const
|
||||
{
|
||||
return std::tr1::static_pointer_cast<const Array>(this->getField());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
const_svector v(this->view());
|
||||
typename const_svector::const_iterator it(v.begin()),
|
||||
end(v.end());
|
||||
o << '[';
|
||||
if(it!=end) {
|
||||
o << print_cast(*it++);
|
||||
for(; it!=end; ++it)
|
||||
o << ',' << print_cast(*it);
|
||||
|
||||
ArrayConstPtr array = this->getArray();
|
||||
if (array->getArraySizeType() == Array::fixed)
|
||||
{
|
||||
// this->setLength(array->getMaximumCapacity());
|
||||
this->setCapacityMutable(false);
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o, size_t index) const
|
||||
{
|
||||
return o << '"' << escape(this->view().at(index)) << '"';
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
const_svector v(this->view());
|
||||
const_svector::const_iterator it(v.begin()),
|
||||
end(v.end());
|
||||
o << '[';
|
||||
if(it!=end) {
|
||||
o << '"' << escape(*it++) << '"';
|
||||
for(; it!=end; ++it)
|
||||
o << ", \"" << escape(*it) << '"';
|
||||
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o, size_t index) const
|
||||
{
|
||||
return o << print_cast(this->view().at(index));
|
||||
}
|
||||
|
||||
DefaultPVArray<T>::~DefaultPVArray()
|
||||
{ }
|
||||
template<typename T>
|
||||
void PVValueArray<T>::setCapacity(size_t capacity)
|
||||
void DefaultPVArray<T>::setCapacity(size_t capacity)
|
||||
{
|
||||
if(this->isCapacityMutable()) {
|
||||
this->checkLength(capacity);
|
||||
@@ -270,7 +277,7 @@ void PVValueArray<T>::setCapacity(size_t capacity)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::setLength(size_t length)
|
||||
void DefaultPVArray<T>::setLength(size_t length)
|
||||
{
|
||||
if(this->isImmutable())
|
||||
THROW_EXCEPTION2(std::logic_error, "immutable");
|
||||
@@ -287,7 +294,7 @@ void PVValueArray<T>::setLength(size_t length)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::replace(const const_svector& next)
|
||||
void DefaultPVArray<T>::replace(const const_svector& next)
|
||||
{
|
||||
this->checkLength(next.size());
|
||||
|
||||
@@ -296,7 +303,7 @@ void PVValueArray<T>::replace(const const_svector& next)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::swap(const_svector &other)
|
||||
void DefaultPVArray<T>::swap(const_svector &other)
|
||||
{
|
||||
if (this->isImmutable())
|
||||
THROW_EXCEPTION2(std::logic_error, "immutable");
|
||||
@@ -308,13 +315,13 @@ void PVValueArray<T>::swap(const_svector &other)
|
||||
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
serialize(pbuffer, pflusher, 0, this->getLength());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pcontrol) {
|
||||
|
||||
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
|
||||
@@ -363,7 +370,7 @@ void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const
|
||||
{
|
||||
//TODO: avoid incrementing the ref counter...
|
||||
@@ -407,7 +414,7 @@ void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
// specializations for string
|
||||
|
||||
template<>
|
||||
void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
void DefaultPVArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pcontrol) {
|
||||
|
||||
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
|
||||
@@ -434,7 +441,7 @@ void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
|
||||
template<>
|
||||
void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
void DefaultPVArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const {
|
||||
|
||||
const_svector temp(value);
|
||||
@@ -450,17 +457,18 @@ void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::_getAsVoid(epics::pvData::shared_vector<const void>& out) const
|
||||
{
|
||||
out = static_shared_vector_cast<const void>(this->view());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::_putFromVoid(const epics::pvData::shared_vector<const void>& in)
|
||||
{
|
||||
this->replace(shared_vector_convert<const T>(in));
|
||||
}
|
||||
typedef DefaultPVArray<boolean> DefaultPVBooleanArray;
|
||||
typedef DefaultPVArray<int8> BasePVByteArray;
|
||||
typedef DefaultPVArray<int16> BasePVShortArray;
|
||||
typedef DefaultPVArray<int32> BasePVIntArray;
|
||||
typedef DefaultPVArray<int64> BasePVLongArray;
|
||||
typedef DefaultPVArray<uint8> BasePVUByteArray;
|
||||
typedef DefaultPVArray<uint16> BasePVUShortArray;
|
||||
typedef DefaultPVArray<uint32> BasePVUIntArray;
|
||||
typedef DefaultPVArray<uint64> BasePVULongArray;
|
||||
typedef DefaultPVArray<float> BasePVFloatArray;
|
||||
typedef DefaultPVArray<double> BasePVDoubleArray;
|
||||
typedef DefaultPVArray<string> BasePVStringArray;
|
||||
|
||||
// Factory
|
||||
|
||||
@@ -555,29 +563,29 @@ PVScalarPtr PVDataCreate::createPVScalar(ScalarConstPtr const & scalar)
|
||||
ScalarType scalarType = scalar->getScalarType();
|
||||
switch(scalarType) {
|
||||
case pvBoolean:
|
||||
return PVScalarPtr(new PVBoolean(scalar));
|
||||
return PVScalarPtr(new BasePVBoolean(scalar));
|
||||
case pvByte:
|
||||
return PVScalarPtr(new PVByte(scalar));
|
||||
return PVScalarPtr(new BasePVByte(scalar));
|
||||
case pvShort:
|
||||
return PVScalarPtr(new PVShort(scalar));
|
||||
return PVScalarPtr(new BasePVShort(scalar));
|
||||
case pvInt:
|
||||
return PVScalarPtr(new PVInt(scalar));
|
||||
return PVScalarPtr(new BasePVInt(scalar));
|
||||
case pvLong:
|
||||
return PVScalarPtr(new PVLong(scalar));
|
||||
return PVScalarPtr(new BasePVLong(scalar));
|
||||
case pvUByte:
|
||||
return PVScalarPtr(new PVUByte(scalar));
|
||||
return PVScalarPtr(new BasePVUByte(scalar));
|
||||
case pvUShort:
|
||||
return PVScalarPtr(new PVUShort(scalar));
|
||||
return PVScalarPtr(new BasePVUShort(scalar));
|
||||
case pvUInt:
|
||||
return PVScalarPtr(new PVUInt(scalar));
|
||||
return PVScalarPtr(new BasePVUInt(scalar));
|
||||
case pvULong:
|
||||
return PVScalarPtr(new PVULong(scalar));
|
||||
return PVScalarPtr(new BasePVULong(scalar));
|
||||
case pvFloat:
|
||||
return PVScalarPtr(new PVFloat(scalar));
|
||||
return PVScalarPtr(new BasePVFloat(scalar));
|
||||
case pvDouble:
|
||||
return PVScalarPtr(new PVDouble(scalar));
|
||||
return PVScalarPtr(new BasePVDouble(scalar));
|
||||
case pvString:
|
||||
return PVScalarPtr(new PVString(scalar));
|
||||
return PVScalarPtr(new BasePVString(scalar));
|
||||
}
|
||||
throw std::logic_error("PVDataCreate::createPVScalar should never get here");
|
||||
}
|
||||
@@ -602,29 +610,29 @@ PVScalarArrayPtr PVDataCreate::createPVScalarArray(
|
||||
{
|
||||
switch(scalarArray->getElementType()) {
|
||||
case pvBoolean:
|
||||
return PVScalarArrayPtr(new PVBooleanArray(scalarArray));
|
||||
return PVScalarArrayPtr(new DefaultPVBooleanArray(scalarArray));
|
||||
case pvByte:
|
||||
return PVScalarArrayPtr(new PVByteArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVByteArray(scalarArray));
|
||||
case pvShort:
|
||||
return PVScalarArrayPtr(new PVShortArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVShortArray(scalarArray));
|
||||
case pvInt:
|
||||
return PVScalarArrayPtr(new PVIntArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVIntArray(scalarArray));
|
||||
case pvLong:
|
||||
return PVScalarArrayPtr(new PVLongArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVLongArray(scalarArray));
|
||||
case pvUByte:
|
||||
return PVScalarArrayPtr(new PVUByteArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVUByteArray(scalarArray));
|
||||
case pvUShort:
|
||||
return PVScalarArrayPtr(new PVUShortArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVUShortArray(scalarArray));
|
||||
case pvUInt:
|
||||
return PVScalarArrayPtr(new PVUIntArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVUIntArray(scalarArray));
|
||||
case pvULong:
|
||||
return PVScalarArrayPtr(new PVULongArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVULongArray(scalarArray));
|
||||
case pvFloat:
|
||||
return PVScalarArrayPtr(new PVFloatArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVFloatArray(scalarArray));
|
||||
case pvDouble:
|
||||
return PVScalarArrayPtr(new PVDoubleArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVDoubleArray(scalarArray));
|
||||
case pvString:
|
||||
return PVScalarArrayPtr(new PVStringArray(scalarArray));
|
||||
return PVScalarArrayPtr(new BasePVStringArray(scalarArray));
|
||||
}
|
||||
throw std::logic_error("PVDataCreate::createPVScalarArray should never get here");
|
||||
|
||||
@@ -715,51 +723,23 @@ PVUnionPtr PVDataCreate::createPVUnion(PVUnionPtr const & unionToClone)
|
||||
return punion;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
struct pvfield_factory {
|
||||
PVDataCreatePtr pvDataCreate;
|
||||
pvfield_factory() :pvDataCreate(new PVDataCreate()) {
|
||||
registerRefCounter("PVField", &PVField::num_instances);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static detail::pvfield_factory* pvfield_factory_s;
|
||||
static epicsThreadOnceId pvfield_factory_once = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static void pvfield_factory_init(void*)
|
||||
// TODO not thread-safe (local static initializers)
|
||||
// TODO replace with non-locking singleton pattern
|
||||
PVDataCreatePtr PVDataCreate::getPVDataCreate()
|
||||
{
|
||||
try {
|
||||
pvfield_factory_s = new detail::pvfield_factory;
|
||||
}catch(std::exception& e){
|
||||
std::cerr<<"Error initializing getFieldCreate() : "<<e.what()<<"\n";
|
||||
}
|
||||
static PVDataCreatePtr pvDataCreate;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
|
||||
if(pvDataCreate.get()==0) pvDataCreate = PVDataCreatePtr(new PVDataCreate());
|
||||
return pvDataCreate;
|
||||
}
|
||||
|
||||
const PVDataCreatePtr& PVDataCreate::getPVDataCreate()
|
||||
{
|
||||
epicsThreadOnce(&pvfield_factory_once, &pvfield_factory_init, 0);
|
||||
if(!pvfield_factory_s->pvDataCreate)
|
||||
throw std::logic_error("getPVDataCreate() not initialized");
|
||||
return pvfield_factory_s->pvDataCreate;
|
||||
PVDataCreatePtr getPVDataCreate() {
|
||||
return PVDataCreate::getPVDataCreate();
|
||||
}
|
||||
|
||||
// explicitly instanciate to ensure that windows
|
||||
// builds emit exported symbols for inline'd methods
|
||||
template class PVScalarValue<boolean>;
|
||||
template class PVScalarValue<int8>;
|
||||
template class PVScalarValue<uint8>;
|
||||
template class PVScalarValue<int16>;
|
||||
template class PVScalarValue<uint16>;
|
||||
template class PVScalarValue<int32>;
|
||||
template class PVScalarValue<uint32>;
|
||||
template class PVScalarValue<int64>;
|
||||
template class PVScalarValue<uint64>;
|
||||
template class PVScalarValue<float>;
|
||||
template class PVScalarValue<double>;
|
||||
template class PVScalarValue<std::string>;
|
||||
|
||||
}} // namespace epics::pvData
|
||||
}}
|
||||
|
||||
namespace std{
|
||||
std::ostream& operator<<(std::ostream& o, const epics::pvData::PVField *ptr)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*PVField.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -11,13 +12,10 @@
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
using std::tr1::const_pointer_cast;
|
||||
using std::size_t;
|
||||
@@ -25,20 +23,16 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
size_t PVField::num_instances;
|
||||
|
||||
PVField::PVField(FieldConstPtr field)
|
||||
: parent(NULL),field(field),
|
||||
: notImplemented("not implemented"),
|
||||
parent(NULL),field(field),
|
||||
fieldOffset(0), nextFieldOffset(0),
|
||||
immutable(false)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
}
|
||||
|
||||
PVField::~PVField()
|
||||
{
|
||||
REFTRACE_DECREMENT(num_instances);
|
||||
}
|
||||
{ }
|
||||
|
||||
|
||||
size_t PVField::getFieldOffset() const
|
||||
@@ -60,16 +54,22 @@ size_t PVField::getNumberFields() const
|
||||
}
|
||||
|
||||
|
||||
bool PVField::isImmutable() const {return immutable;}
|
||||
|
||||
void PVField::setImmutable() {immutable = true;}
|
||||
|
||||
const FieldConstPtr & PVField::getField() const {return field;}
|
||||
|
||||
PVStructure *PVField::getParent() const {return parent;}
|
||||
|
||||
void PVField::postPut()
|
||||
{
|
||||
if(postHandler) postHandler->postPut();
|
||||
if(postHandler.get()!=NULL) postHandler->postPut();
|
||||
}
|
||||
|
||||
void PVField::setPostHandler(PostHandlerPtr const &handler)
|
||||
{
|
||||
if(postHandler) {
|
||||
if(postHandler.get()!=NULL) {
|
||||
if(postHandler.get()==handler.get()) return;
|
||||
throw std::logic_error(
|
||||
"PVField::setPostHandler a postHandler is already registered");
|
||||
@@ -97,7 +97,7 @@ std::ostream& operator<<(std::ostream& o, const PVField& f)
|
||||
string PVField::getFullName() const
|
||||
{
|
||||
string ret(fieldName);
|
||||
for(const PVField *fld=getParent(); fld; fld=fld->getParent())
|
||||
for(PVField *fld=getParent(); fld; fld=fld->getParent())
|
||||
{
|
||||
if(fld->getFieldName().size()==0) break;
|
||||
ret = fld->getFieldName() + '.' + ret;
|
||||
@@ -184,16 +184,62 @@ void PVField::copy(const PVField& from)
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
|
||||
if (getField() != from.getField())
|
||||
if (getField()->getType() != from.getField()->getType())
|
||||
throw std::invalid_argument("field types do not match");
|
||||
|
||||
copyUnchecked(from);
|
||||
switch(getField()->getType())
|
||||
{
|
||||
case scalar:
|
||||
{
|
||||
const PVScalar* fromS = static_cast<const PVScalar*>(&from);
|
||||
PVScalar* toS = static_cast<PVScalar*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case scalarArray:
|
||||
{
|
||||
const PVScalarArray* fromS = static_cast<const PVScalarArray*>(&from);
|
||||
PVScalarArray* toS = static_cast<PVScalarArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case structure:
|
||||
{
|
||||
const PVStructure* fromS = static_cast<const PVStructure*>(&from);
|
||||
PVStructure* toS = static_cast<PVStructure*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case structureArray:
|
||||
{
|
||||
const PVStructureArray* fromS = static_cast<const PVStructureArray*>(&from);
|
||||
PVStructureArray* toS = static_cast<PVStructureArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case union_:
|
||||
{
|
||||
const PVUnion* fromS = static_cast<const PVUnion*>(&from);
|
||||
PVUnion* toS = static_cast<PVUnion*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case unionArray:
|
||||
{
|
||||
const PVUnionArray* fromS = static_cast<const PVUnionArray*>(&from);
|
||||
PVUnionArray* toS = static_cast<PVUnionArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw std::logic_error("PVField::copy unknown type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVField::copyUnchecked(const PVField& from)
|
||||
{
|
||||
assert(getField()==from.getField());
|
||||
|
||||
switch(getField()->getType())
|
||||
{
|
||||
case scalar:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*PVScalar.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -28,4 +29,22 @@ namespace epics { namespace pvData {
|
||||
{
|
||||
return static_pointer_cast<const Scalar>(PVField::getField());
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVScalarValue<int8>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
return o << static_cast<int>(get());
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVScalarValue<uint8>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
return o << static_cast<unsigned int>(get());
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVScalarValue<boolean>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
return o << std::boolalpha << static_cast<bool>(get());
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*PVScalarArray.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*PVStructure.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -24,6 +25,25 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
PVFieldPtr PVStructure::nullPVField;
|
||||
PVBooleanPtr PVStructure::nullPVBoolean;
|
||||
PVBytePtr PVStructure::nullPVByte;
|
||||
PVShortPtr PVStructure::nullPVShort;
|
||||
PVIntPtr PVStructure::nullPVInt;
|
||||
PVLongPtr PVStructure::nullPVLong;
|
||||
PVUBytePtr PVStructure::nullPVUByte;
|
||||
PVUShortPtr PVStructure::nullPVUShort;
|
||||
PVUIntPtr PVStructure::nullPVUInt;
|
||||
PVULongPtr PVStructure::nullPVULong;
|
||||
PVFloatPtr PVStructure::nullPVFloat;
|
||||
PVDoublePtr PVStructure::nullPVDouble;
|
||||
PVStringPtr PVStructure::nullPVString;
|
||||
PVStructurePtr PVStructure::nullPVStructure;
|
||||
PVStructureArrayPtr PVStructure::nullPVStructureArray;
|
||||
PVUnionPtr PVStructure::nullPVUnion;
|
||||
PVUnionArrayPtr PVStructure::nullPVUnionArray;
|
||||
PVScalarArrayPtr PVStructure::nullPVScalarArray;
|
||||
|
||||
PVStructure::PVStructure(StructureConstPtr const & structurePtr)
|
||||
: PVField(structurePtr),
|
||||
structurePtr(structurePtr),
|
||||
@@ -32,6 +52,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr)
|
||||
size_t numberFields = structurePtr->getNumberFields();
|
||||
FieldConstPtrArray const & fields = structurePtr->getFields();
|
||||
StringArray const & fieldNames = structurePtr->getFieldNames();
|
||||
// PVFieldPtrArray * xxx = const_cast<PVFieldPtrArray *>(&pvFields);
|
||||
pvFields.reserve(numberFields);
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
for(size_t i=0; i<numberFields; i++) {
|
||||
@@ -60,7 +81,9 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr,
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::~PVStructure() {}
|
||||
PVStructure::~PVStructure()
|
||||
{
|
||||
}
|
||||
|
||||
void PVStructure::setImmutable()
|
||||
{
|
||||
@@ -72,42 +95,60 @@ void PVStructure::setImmutable()
|
||||
PVField::setImmutable();
|
||||
}
|
||||
|
||||
PVFieldPtr PVStructure::getSubFieldImpl(size_t fieldOffset, bool throws) const
|
||||
StructureConstPtr PVStructure::getStructure() const
|
||||
{
|
||||
const PVStructure *current = this;
|
||||
return structurePtr;
|
||||
}
|
||||
|
||||
recurse:
|
||||
// we don't permit self lookup
|
||||
if(fieldOffset<=current->getFieldOffset() || fieldOffset>=current->getNextFieldOffset()) {
|
||||
if(throws) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field with offset "
|
||||
<< fieldOffset << " (Invalid offset)" ;
|
||||
throw std::runtime_error(ss.str());
|
||||
} else {
|
||||
return PVFieldPtr();
|
||||
const PVFieldPtrArray & PVStructure::getPVFields() const
|
||||
{
|
||||
return pvFields;
|
||||
}
|
||||
|
||||
PVFieldPtr PVStructure::getSubField(string const &fieldName) const
|
||||
{
|
||||
PVField * field = getSubFieldImpl(fieldName.c_str(), false);
|
||||
if (field)
|
||||
return field->shared_from_this();
|
||||
else
|
||||
return PVFieldPtr();
|
||||
}
|
||||
|
||||
|
||||
PVFieldPtr PVStructure::getSubField(size_t fieldOffset) const
|
||||
{
|
||||
if(fieldOffset<=getFieldOffset()) {
|
||||
return nullPVField;
|
||||
}
|
||||
if(fieldOffset>getNextFieldOffset()) return nullPVField;
|
||||
size_t numFields = pvFields.size();
|
||||
for(size_t i=0; i<numFields; i++) {
|
||||
PVFieldPtr pvField = pvFields[i];
|
||||
if(pvField->getFieldOffset()==fieldOffset) return pvFields[i];
|
||||
if(pvField->getNextFieldOffset()<=fieldOffset) continue;
|
||||
if(pvField->getField()->getType()==structure) {
|
||||
PVStructure *pvStructure = static_cast<PVStructure *>(pvField.get());
|
||||
return pvStructure->getSubField(fieldOffset);
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i=0, numFields = current->pvFields.size(); i<numFields; i++) {
|
||||
const PVFieldPtr& pvField = current->pvFields[i];
|
||||
|
||||
if(pvField->getFieldOffset()==fieldOffset) {
|
||||
return pvField;
|
||||
|
||||
} else if(pvField->getNextFieldOffset()<=fieldOffset) {
|
||||
continue;
|
||||
|
||||
} else if(pvField->getField()->getType()==structure) {
|
||||
current = static_cast<PVStructure *>(pvField.get());
|
||||
goto recurse;
|
||||
}
|
||||
}
|
||||
// the first test against current->getNextFieldOffset() would avoid this
|
||||
throw std::logic_error("PVStructure.getSubField: Logic error");
|
||||
}
|
||||
|
||||
PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
PVFieldPtr PVStructure::getSubFieldT(std::size_t fieldOffset) const
|
||||
{
|
||||
PVFieldPtr pvField = getSubField(fieldOffset);
|
||||
if (pvField.get())
|
||||
return pvField;
|
||||
else
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field with offset "
|
||||
<< fieldOffset << "(Invalid offset)" ;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
PVField* PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
{
|
||||
const PVStructure *parent = this;
|
||||
if(!name)
|
||||
@@ -115,7 +156,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
if (throws)
|
||||
throw std::invalid_argument("Failed to get field: (Field name is NULL string)");
|
||||
else
|
||||
return PVFieldPtr();
|
||||
return NULL;
|
||||
}
|
||||
const char *fullName = name;
|
||||
while(true) {
|
||||
@@ -131,7 +172,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
return NULL;
|
||||
}
|
||||
size_t N = sep-name;
|
||||
if(N==0)
|
||||
@@ -144,7 +185,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& pvFields = parent->getPVFields();
|
||||
@@ -171,7 +212,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(*sep) {
|
||||
@@ -188,32 +229,18 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
return NULL;
|
||||
}
|
||||
child = NULL;
|
||||
name = sep+1; // skip past '.'
|
||||
// loop around to new parent
|
||||
|
||||
} else {
|
||||
return child->shared_from_this();
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVStructure::throwBadFieldType(const char *name)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Failed to get field: " << name << " (Field has wrong type)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
void PVStructure::throwBadFieldType(std::size_t fieldOffset)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field with offset "
|
||||
<< fieldOffset << " (Field has wrong type)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
@@ -232,8 +259,9 @@ void PVStructure::deserialize(ByteBuffer *pbuffer,
|
||||
|
||||
void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, BitSet *pbitSet) const {
|
||||
size_t numberFields = this->getNumberFields();
|
||||
size_t offset = this->getFieldOffset();
|
||||
PVStructure* nonConstThis = const_cast<PVStructure*>(this);
|
||||
size_t numberFields = nonConstThis->getNumberFields();
|
||||
size_t offset = nonConstThis->getFieldOffset();
|
||||
int32 next = pbitSet->nextSetBit(static_cast<uint32>(offset));
|
||||
|
||||
// no more changes or no changes in this structure
|
||||
@@ -247,7 +275,7 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
|
||||
size_t fieldsSize = pvFields.size();
|
||||
for(size_t i = 0; i<fieldsSize; i++) {
|
||||
PVField* pvField = pvFields[i].get();
|
||||
PVFieldPtr pvField = pvFields[i];
|
||||
offset = pvField->getFieldOffset();
|
||||
int32 inumberFields = static_cast<int32>(pvField->getNumberFields());
|
||||
next = pbitSet->nextSetBit(static_cast<uint32>(offset));
|
||||
@@ -261,7 +289,8 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
if(inumberFields==1) {
|
||||
pvField->serialize(pbuffer, pflusher);
|
||||
} else {
|
||||
static_cast<PVStructure*>(pvField)->serialize(pbuffer, pflusher, pbitSet);
|
||||
PVStructurePtr pvStructure = std::tr1::static_pointer_cast<PVStructure>(pvField);
|
||||
pvStructure->serialize(pbuffer, pflusher, pbitSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*PVStructureArray.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -76,14 +77,14 @@ void PVStructureArray::compress() {
|
||||
size_t newLength = 0;
|
||||
|
||||
for(size_t i=0; i<length; i++) {
|
||||
if(vec[i]) {
|
||||
if(vec[i].get()!=NULL) {
|
||||
newLength++;
|
||||
continue;
|
||||
}
|
||||
// find first non 0
|
||||
size_t notNull = 0;
|
||||
for(size_t j=i+1;j<length;j++) {
|
||||
if(vec[j]) {
|
||||
if(vec[j].get()!=NULL) {
|
||||
notNull = j;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*PVUnion.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
@@ -25,7 +26,7 @@ using std::string;
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
#define PVUNION_UNDEFINED_INDEX -1
|
||||
const int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
|
||||
int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
|
||||
|
||||
PVDataCreatePtr PVUnion::pvDataCreate(getPVDataCreate());
|
||||
|
||||
@@ -40,7 +41,24 @@ PVUnion::PVUnion(UnionConstPtr const & unionPtr)
|
||||
|
||||
#undef PVUNION_UNDEFINED_INDEX
|
||||
|
||||
PVUnion::~PVUnion() {}
|
||||
PVUnion::~PVUnion()
|
||||
{
|
||||
}
|
||||
|
||||
UnionConstPtr PVUnion::getUnion() const
|
||||
{
|
||||
return unionPtr;
|
||||
}
|
||||
|
||||
PVFieldPtr PVUnion::get() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
int32 PVUnion::getSelectedIndex() const
|
||||
{
|
||||
return selector;
|
||||
}
|
||||
|
||||
string PVUnion::getSelectedFieldName() const
|
||||
{
|
||||
@@ -53,11 +71,8 @@ string PVUnion::getSelectedFieldName() const
|
||||
|
||||
PVFieldPtr PVUnion::select(int32 index)
|
||||
{
|
||||
if (variant && index != UNDEFINED_INDEX)
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
|
||||
// no change
|
||||
if (selector == index && !variant)
|
||||
if (selector == index)
|
||||
return value;
|
||||
|
||||
if (index == UNDEFINED_INDEX)
|
||||
@@ -66,7 +81,9 @@ PVFieldPtr PVUnion::select(int32 index)
|
||||
value.reset();
|
||||
return value;
|
||||
}
|
||||
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
|
||||
else if (variant)
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
|
||||
FieldConstPtr field = unionPtr->getField(index);
|
||||
@@ -83,6 +100,11 @@ PVFieldPtr PVUnion::select(string const & fieldName)
|
||||
throw std::invalid_argument("no such fieldName");
|
||||
return select(index);
|
||||
}
|
||||
|
||||
void PVUnion::set(PVFieldPtr const & value)
|
||||
{
|
||||
set(selector, value);
|
||||
}
|
||||
|
||||
void PVUnion::set(int32 index, PVFieldPtr const & value)
|
||||
{
|
||||
@@ -93,14 +115,14 @@ void PVUnion::set(int32 index, PVFieldPtr const & value)
|
||||
if (index == UNDEFINED_INDEX)
|
||||
{
|
||||
// for undefined index we accept only null values
|
||||
if (value)
|
||||
if (value.get())
|
||||
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
|
||||
}
|
||||
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
|
||||
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
else if (!value)
|
||||
throw std::invalid_argument("Can't set defined index w/ NULL");
|
||||
else if (value->getField() != unionPtr->getField(index))
|
||||
|
||||
// value type must match
|
||||
if (value->getField() != unionPtr->getField(index))
|
||||
throw std::invalid_argument("selected field and its introspection data do not match");
|
||||
}
|
||||
|
||||
@@ -115,7 +137,7 @@ void PVUnion::set(string const & fieldName, PVFieldPtr const & value)
|
||||
if (index == -1)
|
||||
throw std::invalid_argument("no such fieldName");
|
||||
|
||||
set(index, value);
|
||||
set(index, value);
|
||||
}
|
||||
|
||||
void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) const
|
||||
@@ -123,10 +145,10 @@ void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) cons
|
||||
if (variant)
|
||||
{
|
||||
// write introspection data
|
||||
if (value.get() == 0) {
|
||||
pflusher->ensureBuffer(1);
|
||||
if (value.get() == 0)
|
||||
pbuffer->put((int8)-1);
|
||||
}else {
|
||||
else
|
||||
{
|
||||
pflusher->cachedSerialize(value->getField(), pbuffer);
|
||||
value->serialize(pbuffer, pflusher);
|
||||
}
|
||||
@@ -181,7 +203,7 @@ std::ostream& PVUnion::dumpValue(std::ostream& o) const
|
||||
o << format::indent() << getUnion()->getID() << ' ' << getFieldName() << std::endl;
|
||||
{
|
||||
format::indent_scope s(o);
|
||||
const PVField::const_shared_pointer& fieldField = get();
|
||||
PVFieldPtr fieldField = get();
|
||||
if (fieldField.get() == NULL)
|
||||
o << format::indent() << "(none)" << std::endl;
|
||||
else
|
||||
@@ -210,7 +232,7 @@ void PVUnion::copy(const PVUnion& from)
|
||||
void PVUnion::copyUnchecked(const PVUnion& from)
|
||||
{
|
||||
|
||||
const PVField::const_shared_pointer& fromValue = from.get();
|
||||
PVFieldPtr fromValue = from.get();
|
||||
if (from.getUnion()->isVariant())
|
||||
{
|
||||
if (fromValue.get() == 0)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*PVUnionArray.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -76,14 +77,14 @@ void PVUnionArray::compress() {
|
||||
size_t newLength = 0;
|
||||
|
||||
for(size_t i=0; i<length; i++) {
|
||||
if(vec[i]) {
|
||||
if(vec[i].get()!=NULL) {
|
||||
newLength++;
|
||||
continue;
|
||||
}
|
||||
// find first non 0
|
||||
size_t notNull = 0;
|
||||
for(size_t j=i+1;j<length;j++) {
|
||||
if(vec[j]) {
|
||||
if(vec[j].get()!=NULL) {
|
||||
notNull = j;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* StandardField.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -10,9 +11,6 @@
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
@@ -23,86 +21,33 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static
|
||||
StructureConstPtr buildValueAlarm(ScalarType vtype)
|
||||
{
|
||||
return FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("lowAlarmLimit", vtype)
|
||||
->add("lowWarningLimit", vtype)
|
||||
->add("highWarningLimit", vtype)
|
||||
->add("highAlarmLimit", vtype)
|
||||
->add("lowAlarmSeverity", pvInt)
|
||||
->add("lowWarningSeverity", pvInt)
|
||||
->add("highWarningSeverity", pvInt)
|
||||
->add("highAlarmSeverity", pvInt)
|
||||
->add("hysteresis", pvByte)
|
||||
->createStructure();
|
||||
}
|
||||
|
||||
StandardField::StandardField()
|
||||
:fieldCreate(getFieldCreate())
|
||||
,notImplemented("not implemented")
|
||||
,valueFieldName("value")
|
||||
|
||||
,alarmField(FieldBuilder::begin()
|
||||
->setId("alarm_t")
|
||||
->add("severity", pvInt)
|
||||
->add("status", pvInt)
|
||||
->add("message", pvString)
|
||||
->createStructure())
|
||||
|
||||
,timeStampField(FieldBuilder::begin()
|
||||
->setId("time_t")
|
||||
->add("secondsPastEpoch", pvLong)
|
||||
->add("nanoseconds", pvInt)
|
||||
->add("userTag", pvInt)
|
||||
->createStructure())
|
||||
|
||||
,displayField(FieldBuilder::begin()
|
||||
->setId("display_t")
|
||||
->add("limitLow", pvDouble)
|
||||
->add("limitHigh", pvDouble)
|
||||
->add("description", pvString)
|
||||
->add("format", pvString)
|
||||
->add("units", pvString)
|
||||
->createStructure())
|
||||
|
||||
,controlField(FieldBuilder::begin()
|
||||
->setId("control_t")
|
||||
->add("limitLow", pvDouble)
|
||||
->add("limitHigh", pvDouble)
|
||||
->add("minStep", pvDouble)
|
||||
->createStructure())
|
||||
|
||||
,booleanAlarmField(FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("falseSeverity", pvInt)
|
||||
->add("trueSeverity", pvInt)
|
||||
->add("changeStateSeverity", pvInt)
|
||||
->createStructure())
|
||||
|
||||
,byteAlarmField(buildValueAlarm(pvByte))
|
||||
,shortAlarmField(buildValueAlarm(pvShort))
|
||||
,intAlarmField(buildValueAlarm(pvInt))
|
||||
,longAlarmField(buildValueAlarm(pvLong))
|
||||
,ubyteAlarmField(buildValueAlarm(pvUByte))
|
||||
,ushortAlarmField(buildValueAlarm(pvUShort))
|
||||
,uintAlarmField(buildValueAlarm(pvUInt))
|
||||
,ulongAlarmField(buildValueAlarm(pvULong))
|
||||
,floatAlarmField(buildValueAlarm(pvFloat))
|
||||
,doubleAlarmField(buildValueAlarm(pvDouble))
|
||||
|
||||
,enumeratedAlarmField(FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("stateSeverity", pvInt)
|
||||
->add("changeStateSeverity", pvInt)
|
||||
->createStructure())
|
||||
: fieldCreate(getFieldCreate()),
|
||||
notImplemented("not implemented"),
|
||||
valueFieldName("value")
|
||||
{}
|
||||
|
||||
void StandardField::init()
|
||||
{
|
||||
createAlarm();
|
||||
createTimeStamp();
|
||||
createDisplay();
|
||||
createControl();
|
||||
createBooleanAlarm();
|
||||
createByteAlarm();
|
||||
createShortAlarm();
|
||||
createIntAlarm();
|
||||
createLongAlarm();
|
||||
createUByteAlarm();
|
||||
createUShortAlarm();
|
||||
createUIntAlarm();
|
||||
createULongAlarm();
|
||||
createFloatAlarm();
|
||||
createDoubleAlarm();
|
||||
createEnumeratedAlarm();
|
||||
}
|
||||
|
||||
StandardField::~StandardField(){}
|
||||
|
||||
StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,string properties)
|
||||
@@ -199,6 +144,361 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
|
||||
return fieldCreate->createStructure(id,names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createAlarm() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "severity";
|
||||
names[1] = "status";
|
||||
names[2] = "message";
|
||||
fields[0] = fieldCreate->createScalar(pvInt);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvString);
|
||||
alarmField = fieldCreate->createStructure("alarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createTimeStamp() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "secondsPastEpoch";
|
||||
names[1] = "nanoseconds";
|
||||
names[2] = "userTag";
|
||||
fields[0] = fieldCreate->createScalar(pvLong);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
timeStampField = fieldCreate->createStructure("time_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createDisplay() {
|
||||
size_t num = 5;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "limitLow";
|
||||
names[1] = "limitHigh";
|
||||
names[2] = "description";
|
||||
names[3] = "format";
|
||||
names[4] = "units";
|
||||
fields[0] = fieldCreate->createScalar(pvDouble);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvString);
|
||||
fields[3] = fieldCreate->createScalar(pvString);
|
||||
fields[4] = fieldCreate->createScalar(pvString);
|
||||
displayField = fieldCreate->createStructure("display_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createControl() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "limitLow";
|
||||
names[1] = "limitHigh";
|
||||
names[2] = "minStep";
|
||||
fields[0] = fieldCreate->createScalar(pvDouble);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvDouble);
|
||||
controlField = fieldCreate->createStructure("control_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createBooleanAlarm() {
|
||||
size_t numFields = 4;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "falseSeverity";
|
||||
names[2] = "trueSeverity";
|
||||
names[3] = "changeStateSeverity";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
fields[3] = fieldCreate->createScalar(pvInt);
|
||||
booleanAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createByteAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvByte);
|
||||
fields[2] = fieldCreate->createScalar(pvByte);
|
||||
fields[3] = fieldCreate->createScalar(pvByte);
|
||||
fields[4] = fieldCreate->createScalar(pvByte);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvByte);
|
||||
byteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createShortAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvShort);
|
||||
fields[2] = fieldCreate->createScalar(pvShort);
|
||||
fields[3] = fieldCreate->createScalar(pvShort);
|
||||
fields[4] = fieldCreate->createScalar(pvShort);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvShort);
|
||||
shortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createIntAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
fields[3] = fieldCreate->createScalar(pvInt);
|
||||
fields[4] = fieldCreate->createScalar(pvInt);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvInt);
|
||||
intAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createLongAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvLong);
|
||||
fields[2] = fieldCreate->createScalar(pvLong);
|
||||
fields[3] = fieldCreate->createScalar(pvLong);
|
||||
fields[4] = fieldCreate->createScalar(pvLong);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvLong);
|
||||
longAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUByteAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUByte);
|
||||
fields[2] = fieldCreate->createScalar(pvUByte);
|
||||
fields[3] = fieldCreate->createScalar(pvUByte);
|
||||
fields[4] = fieldCreate->createScalar(pvUByte);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUByte);
|
||||
ubyteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUShortAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUShort);
|
||||
fields[2] = fieldCreate->createScalar(pvUShort);
|
||||
fields[3] = fieldCreate->createScalar(pvUShort);
|
||||
fields[4] = fieldCreate->createScalar(pvUShort);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUShort);
|
||||
ushortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUIntAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUInt);
|
||||
fields[2] = fieldCreate->createScalar(pvUInt);
|
||||
fields[3] = fieldCreate->createScalar(pvUInt);
|
||||
fields[4] = fieldCreate->createScalar(pvUInt);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUInt);
|
||||
uintAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createULongAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvULong);
|
||||
fields[2] = fieldCreate->createScalar(pvULong);
|
||||
fields[3] = fieldCreate->createScalar(pvULong);
|
||||
fields[4] = fieldCreate->createScalar(pvULong);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvULong);
|
||||
ulongAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createFloatAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvFloat);
|
||||
fields[2] = fieldCreate->createScalar(pvFloat);
|
||||
fields[3] = fieldCreate->createScalar(pvFloat);
|
||||
fields[4] = fieldCreate->createScalar(pvFloat);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvFloat);
|
||||
floatAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createDoubleAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvDouble);
|
||||
fields[3] = fieldCreate->createScalar(pvDouble);
|
||||
fields[4] = fieldCreate->createScalar(pvDouble);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvDouble);
|
||||
doubleAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createEnumeratedAlarm() {
|
||||
size_t numFields = 3;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "stateSeverity";
|
||||
names[2] = "changeStateSeverity";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalarArray(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
enumeratedAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
|
||||
StructureConstPtr StandardField::scalar(
|
||||
ScalarType type,string const &properties)
|
||||
{
|
||||
@@ -263,21 +563,104 @@ StructureConstPtr StandardField::enumerated(string const &properties)
|
||||
return createProperties("epics:nt/NTEnum:1.0",field,properties);
|
||||
}
|
||||
|
||||
static StandardFieldPtr *stdFieldGbl;
|
||||
|
||||
static epicsThreadOnceId stdFieldGblOnce = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
void StandardField::once(void*)
|
||||
StructureConstPtr StandardField::alarm()
|
||||
{
|
||||
stdFieldGbl = new StandardFieldPtr;
|
||||
stdFieldGbl->reset(new StandardField);
|
||||
return alarmField;
|
||||
}
|
||||
|
||||
const StandardFieldPtr &StandardField::getStandardField()
|
||||
StructureConstPtr StandardField::timeStamp()
|
||||
{
|
||||
epicsThreadOnce(&stdFieldGblOnce, &StandardField::once, 0);
|
||||
return timeStampField;
|
||||
}
|
||||
|
||||
return *stdFieldGbl;
|
||||
StructureConstPtr StandardField::display()
|
||||
{
|
||||
return displayField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::control()
|
||||
{
|
||||
return controlField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::booleanAlarm()
|
||||
{
|
||||
return booleanAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::byteAlarm()
|
||||
{
|
||||
return byteAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::ubyteAlarm()
|
||||
{
|
||||
return ubyteAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::shortAlarm()
|
||||
{
|
||||
return shortAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::ushortAlarm()
|
||||
{
|
||||
return ushortAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::intAlarm()
|
||||
{
|
||||
return intAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::uintAlarm()
|
||||
{
|
||||
return uintAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::longAlarm()
|
||||
{
|
||||
return longAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::ulongAlarm()
|
||||
{
|
||||
return ulongAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::floatAlarm()
|
||||
{
|
||||
return floatAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::doubleAlarm()
|
||||
{
|
||||
return doubleAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::enumeratedAlarm()
|
||||
{
|
||||
return enumeratedAlarmField;
|
||||
}
|
||||
|
||||
StandardFieldPtr StandardField::getStandardField()
|
||||
{
|
||||
static StandardFieldPtr standardFieldCreate;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
|
||||
if(standardFieldCreate.get()==0)
|
||||
{
|
||||
standardFieldCreate = StandardFieldPtr(new StandardField());
|
||||
standardFieldCreate->init();
|
||||
}
|
||||
return standardFieldCreate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
StandardFieldPtr getStandardField() {
|
||||
return StandardField::getStandardField();
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* StandardPVField.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -9,8 +10,6 @@
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
@@ -69,7 +68,7 @@ PVStructurePtr StandardPVField::enumerated(StringArray const &choices)
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(field);
|
||||
PVStringArray::svector cdata(choices.size());
|
||||
std::copy(choices.begin(), choices.end(), cdata.begin());
|
||||
pvStructure->getSubFieldT<PVStringArray>("choices")->replace(freeze(cdata));
|
||||
pvStructure->getSubField<PVStringArray>("choices")->replace(freeze(cdata));
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
@@ -80,7 +79,7 @@ PVStructurePtr StandardPVField::enumerated(
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(field);
|
||||
PVStringArray::svector cdata(choices.size());
|
||||
std::copy(choices.begin(), choices.end(), cdata.begin());
|
||||
pvStructure->getSubFieldT<PVStringArray>("value.choices")->replace(freeze(cdata));
|
||||
pvStructure->getSubField<PVStringArray>("value.choices")->replace(freeze(cdata));
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*TypeFunc.cpp*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
|
||||
18
src/factory/factory.h
Normal file
18
src/factory/factory.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*factory.h*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef FACTORY_H
|
||||
#define FACTORY_H
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
enum DebugLevel{noDebug,lowDebug,highDebug};
|
||||
|
||||
}}
|
||||
#endif /*FACTORY_H */
|
||||
@@ -1,523 +1,45 @@
|
||||
/* printer.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#define HAVE_ISATTY
|
||||
#endif
|
||||
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <epicsTime.h>
|
||||
#include <epicsString.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
# include <pv/json.h>
|
||||
#endif
|
||||
#include <pv/pvIntrospect.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
namespace format
|
||||
{
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
return ios.iword(indent_index);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent const&)
|
||||
{
|
||||
long il = indent_value(os);
|
||||
for(long i=0, spaces = il * 4; i<spaces; i++)
|
||||
os.put(' ');
|
||||
return os;
|
||||
}
|
||||
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
|
||||
bool useEscapes(std::ostream& strm) {
|
||||
FILE *fp = 0;
|
||||
// TODO: a better way to do this...
|
||||
if(&std::cout==&strm)
|
||||
fp = stdout;
|
||||
if(&std::cerr==&strm)
|
||||
fp = stderr;
|
||||
if(!fp) return false;
|
||||
#ifdef HAVE_ISATTY
|
||||
// check $TERM as well?
|
||||
return isatty(fileno(fp))==1;
|
||||
#else
|
||||
// TODO: in theory windows 10 can be made to understand escapes as well
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void printAlarmTx(std::ostream& strm, const PVStructure& sub)
|
||||
{
|
||||
PVScalar::const_shared_pointer pvSeverity(sub.getSubField<PVInt>("severity"));
|
||||
PVScalar::const_shared_pointer pvStatus(sub.getSubField<PVInt>("status"));
|
||||
PVString::const_shared_pointer pvMessage(sub.getSubField<PVString>("message"));
|
||||
|
||||
switch(pvSeverity ? pvSeverity->getAs<int32>() : 0) {
|
||||
case 0: return; // nothing more to do here...
|
||||
case 1: strm<<"MINOR "; break;
|
||||
case 2: strm<<"MAJOR "; break;
|
||||
case 3: strm<<"INVALID "; break;
|
||||
case 4: strm<<"UNDEFINED "; break;
|
||||
default: strm<<pvSeverity->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
switch(pvStatus ? pvStatus->getAs<int32>() : 0) {
|
||||
case 0: break;
|
||||
case 1: strm<<"DEVICE "; break;
|
||||
case 2: strm<<"DRIVER "; break;
|
||||
case 3: strm<<"RECORD "; break;
|
||||
case 4: strm<<"DB "; break;
|
||||
case 5: strm<<"CONF "; break;
|
||||
case 6: strm<<"UNDEFINED "; break;
|
||||
case 7: strm<<"CLIENT "; break;
|
||||
default: strm<<pvStatus->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
if(pvMessage && !pvMessage->get().empty())
|
||||
strm<<pvMessage->get()<<' ';
|
||||
}
|
||||
|
||||
|
||||
void printAlarmT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("alarm"));
|
||||
if(sub)
|
||||
printAlarmTx(strm, *sub);
|
||||
}
|
||||
|
||||
void printTimeTx(std::ostream& strm, const PVStructure& tsubop)
|
||||
{
|
||||
char timeText[32];
|
||||
epicsTimeStamp epicsTS;
|
||||
|
||||
PVScalar::const_shared_pointer secf(tsubop.getSubField<PVScalar>("secondsPastEpoch")),
|
||||
nsecf(tsubop.getSubField<PVScalar>("nanoseconds")),
|
||||
tagf(tsubop.getSubField<PVScalar>("userTag"));
|
||||
|
||||
epicsTS.secPastEpoch = secf ? secf->getAs<int64>() : 0;
|
||||
epicsTS.nsec = nsecf ? nsecf->getAs<int32>() : 0;
|
||||
|
||||
if(epicsTS.secPastEpoch > POSIX_TIME_AT_EPICS_EPOCH)
|
||||
epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH;
|
||||
else
|
||||
epicsTS.secPastEpoch = 0;
|
||||
|
||||
epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%d %H:%M:%S.%03f", &epicsTS);
|
||||
strm <<std::setw(24) <<std::left <<timeText <<' ';
|
||||
if (tagf) {
|
||||
int64 tagv = tagf->getAs<int64>();
|
||||
if(tagv)
|
||||
strm << tagv << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printTimeT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("timeStamp"));
|
||||
if(sub)
|
||||
printTimeTx(strm, *sub);
|
||||
}
|
||||
|
||||
bool printEnumT(std::ostream& strm, const PVStructure& top, bool fromtop)
|
||||
{
|
||||
PVStructure::const_shared_pointer value;
|
||||
if(fromtop) {
|
||||
value = top.getSubField<PVStructure>("value");
|
||||
} else {
|
||||
value = std::tr1::static_pointer_cast<const PVStructure>(top.shared_from_this());
|
||||
}
|
||||
PVScalar::const_shared_pointer idx(value->getSubField<PVScalar>("index"));
|
||||
PVStringArray::const_shared_pointer choices(value->getSubField<PVStringArray>("choices"));
|
||||
if(!idx || !choices) return false;
|
||||
|
||||
if(fromtop) {
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, top);
|
||||
printAlarmT(strm, top);
|
||||
}
|
||||
|
||||
PVStringArray::const_svector ch(choices->view());
|
||||
uint32 I = idx->getAs<uint32>();
|
||||
strm<<'('<<I<<')';
|
||||
if(I>=ch.size()) {
|
||||
strm<<" <undefined>";
|
||||
} else {
|
||||
strm<<' '<<escape(ch[I]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void csvEscape(std::string& S)
|
||||
{
|
||||
// concise, not particularly efficient...
|
||||
std::string temp(escape(S).style(escape::CSV).str());
|
||||
if(S.find_first_of(" ,\\")!=S.npos) {// only quote if necessary (stupid Excel)
|
||||
std::string temp2;
|
||||
temp2.reserve(temp.size()+2);
|
||||
temp2.push_back('\"');
|
||||
temp2 += temp;
|
||||
temp2.push_back('\"');
|
||||
temp2.swap(temp);
|
||||
}
|
||||
S = temp;
|
||||
}
|
||||
|
||||
bool printTable(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer cf(top.getSubField<PVStructure>("value"));
|
||||
if(!cf) return false;
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
const FieldConstPtrArray& fields = cf->getStructure()->getFields();
|
||||
if(fields.empty()) return false;
|
||||
for(size_t i=0, N=fields.size(); i<N; i++) {
|
||||
if(fields[i]->getType()!=scalarArray)
|
||||
return false; // cowardly refusing to handle anything else...
|
||||
}
|
||||
return ios.iword(indent_index);
|
||||
}
|
||||
|
||||
// maybe output a line with meta-data
|
||||
{
|
||||
bool havets = !!top.getSubField("timeStamp");
|
||||
bool haveal = !!top.getSubField("alarm");
|
||||
if(havets || haveal)
|
||||
strm<<format::indent();
|
||||
if(havets) {
|
||||
printTimeT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
if(haveal) {
|
||||
printAlarmT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
|
||||
PVStringArray::svector labels;
|
||||
{
|
||||
PVStringArray::const_shared_pointer lf(top.getSubField<PVStringArray>("labels"));
|
||||
if(lf) {
|
||||
PVStringArray::const_svector L(lf->view());
|
||||
labels = thaw(L);
|
||||
}
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, indent const&)
|
||||
{
|
||||
long il = indent_value(os);
|
||||
std::size_t spaces = static_cast<std::size_t>(il) * 4;
|
||||
return os << string(spaces, ' ');
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& columns = cf->getPVFields();
|
||||
std::vector<shared_vector<std::string> > coldat(columns.size());
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<size_t> widths(columns.size());
|
||||
labels.reserve(columns.size());
|
||||
|
||||
size_t nrows = size_t(-1);
|
||||
|
||||
for(size_t i=0, N=columns.size(); i<N; i++) {
|
||||
if(i>=labels.size()) {
|
||||
labels.push_back(cf->getStructure()->getFieldName(i));
|
||||
} else {
|
||||
csvEscape(labels[i]);
|
||||
}
|
||||
widths[i] = labels[i].size();
|
||||
|
||||
{
|
||||
shared_vector<const std::string> ro;
|
||||
static_cast<const PVScalarArray*>(columns[i].get())->getAs(ro);
|
||||
coldat[i] = thaw(ro);
|
||||
}
|
||||
|
||||
// truncate if some columns are longer than others
|
||||
nrows = std::min(nrows, coldat[i].size());
|
||||
|
||||
for(size_t j=0, M=coldat[i].size(); j<M; j++) {
|
||||
csvEscape(coldat[i][j]);
|
||||
widths[i] = std::max(widths[i], coldat[i][j].size());
|
||||
}
|
||||
}
|
||||
|
||||
// output header line
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<labels[c];
|
||||
if(c+1!=N) {
|
||||
strm<<' ';
|
||||
}
|
||||
}
|
||||
strm<<'\n';
|
||||
|
||||
for(size_t r=0; r<nrows; r++) {
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<coldat[c][r];
|
||||
if(c+1!=N)
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void expandBS(const PVStructure& top, BitSet& mask, bool parents) {
|
||||
if(mask.get(0)) { // special handling because getSubField(0) not allowed
|
||||
// wildcard
|
||||
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
|
||||
mask.set(idx);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
|
||||
PVField::const_shared_pointer fld = top.getSubFieldT(idx);
|
||||
|
||||
// look forward and mark all children
|
||||
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
|
||||
mask.set(i);
|
||||
|
||||
if(parents) {
|
||||
// look back and mark all parents
|
||||
// we've already stepped past all parents so siblings will not be automatically marked
|
||||
for(const PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
|
||||
mask.set(parent->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur)
|
||||
{
|
||||
typedef std::deque<std::pair<const PVField*, size_t> > todo_t;
|
||||
todo_t todo;
|
||||
BitSet show, highlight;
|
||||
|
||||
const long orig_indent = format::indent_value(strm);
|
||||
|
||||
{
|
||||
if(format.xshow)
|
||||
show = *format.xshow;
|
||||
else
|
||||
show.set(0);
|
||||
|
||||
if(format.xhighlight)
|
||||
highlight = *format.xhighlight;
|
||||
|
||||
expandBS(format.xtop, show, true);
|
||||
expandBS(format.xtop, highlight, false);
|
||||
highlight &= show; // can't highlight hidden fields (paranoia)
|
||||
}
|
||||
|
||||
if(!show.get(0)) return; // nothing to do here...
|
||||
|
||||
todo.push_front(std::make_pair(&format.xtop, orig_indent));
|
||||
|
||||
while(!todo.empty()) {
|
||||
todo_t::value_type cur(todo.front());
|
||||
todo.pop_front();
|
||||
|
||||
format::indent_value(strm) = cur.second;
|
||||
|
||||
bool hl = highlight.get(cur.first->getFieldOffset()) && format.xmode==PVStructure::Formatter::ANSI;
|
||||
if(hl)
|
||||
strm<<"\x1b[1m"; // Bold
|
||||
|
||||
switch(cur.first->getField()->getType()) {
|
||||
case structure: {
|
||||
const PVStructure* str = static_cast<const PVStructure*>(cur.first);
|
||||
|
||||
const PVFieldPtrArray& flds = str->getPVFields();
|
||||
|
||||
for(PVFieldPtrArray::const_reverse_iterator it(flds.rbegin()), end(flds.rend()); it!=end; ++it) {
|
||||
const PVField *fld = (*it).get();
|
||||
if(!show.get(fld->getFieldOffset())) continue;
|
||||
|
||||
todo.push_front(std::make_pair(fld, cur.second+1));
|
||||
}
|
||||
|
||||
std::string id(cur.first->getField()->getID());
|
||||
|
||||
strm<<format::indent()<<id<<' '<<cur.first->getFieldName();
|
||||
if(id=="alarm_t") {
|
||||
strm.put(' ');
|
||||
printAlarmTx(strm, *str);
|
||||
} else if(id=="time_t") {
|
||||
strm.put(' ');
|
||||
printTimeTx(strm, *str);
|
||||
} else if(id=="enum_t") {
|
||||
strm.put(' ');
|
||||
printEnumT(strm, *str, false);
|
||||
}
|
||||
strm.put('\n');
|
||||
}
|
||||
break;
|
||||
case scalar:
|
||||
case scalarArray:
|
||||
strm<<format::indent()<<cur.first->getField()->getID()<<' '<<cur.first->getFieldName()
|
||||
<<' '<<*cur.first
|
||||
<<'\n';
|
||||
break;
|
||||
case structureArray:
|
||||
case union_:
|
||||
case unionArray:
|
||||
strm<<*cur.first;
|
||||
break;
|
||||
}
|
||||
|
||||
if(hl)
|
||||
strm<<"\x1b[0m"; // reset
|
||||
}
|
||||
|
||||
format::indent_value(strm) = orig_indent;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format)
|
||||
{
|
||||
if(format.xfmt==PVStructure::Formatter::JSON) {
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
JSONPrintOptions opts;
|
||||
opts.multiLine = false;
|
||||
printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
#else
|
||||
// fall through to Raw
|
||||
#endif
|
||||
|
||||
} else if(format.xfmt==PVStructure::Formatter::NT) {
|
||||
std::string id(format.xtop.getStructure()->getID()),
|
||||
idprefix(id.substr(0, id.find_first_of('.')));
|
||||
|
||||
// NTTable
|
||||
if(idprefix=="epics:nt/NTTable:1") {
|
||||
if(printTable(strm, format.xtop))
|
||||
return strm;
|
||||
} else {
|
||||
//NTScalar, NTScalarArray, NTEnum, or anything with '.value'
|
||||
|
||||
PVField::const_shared_pointer value(format.xtop.getSubField("value"));
|
||||
if(value) {
|
||||
switch(value->getField()->getType()) {
|
||||
case scalar:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalar*>(value.get())<<' ';
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
|
||||
case scalarArray:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
|
||||
return strm;
|
||||
|
||||
case structure:
|
||||
if(printEnumT(strm, format.xtop, true)) {
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fall through unhandled as Raw
|
||||
|
||||
PVStructure::Formatter format2(format);
|
||||
|
||||
if(format2.xmode==PVStructure::Formatter::Auto)
|
||||
format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain;
|
||||
|
||||
printRaw(strm, format2, format.xtop);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
static char hexdigit(char c) {
|
||||
c &= 0xf;
|
||||
if(c<9)
|
||||
return '0'+c;
|
||||
else
|
||||
return 'A'+c-10;
|
||||
}
|
||||
|
||||
escape::~escape() {}
|
||||
|
||||
std::string escape::str() const
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm<<(*this);
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const escape& Q)
|
||||
{
|
||||
for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) {
|
||||
const char C = Q.orig[pos];
|
||||
char quote = '\\', next;
|
||||
// compare me with epicsStrnEscapedFromRaw()
|
||||
switch(C) {
|
||||
case '\a': next = 'a'; break;
|
||||
case '\b': next = 'b'; break;
|
||||
case '\f': next = 'f'; break;
|
||||
case '\n': next = 'n'; break;
|
||||
case '\r': next = 'r'; break;
|
||||
case '\t': next = 't'; break;
|
||||
case '\v': next = 'v'; break;
|
||||
case '\\': next = '\\'; break;
|
||||
case '\'': next = '\''; break;
|
||||
case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break;
|
||||
default:
|
||||
if(!isprint(C)) {
|
||||
// print three charator escape
|
||||
strm<<"\\x"<<hexdigit(C>>4)<<hexdigit(C);
|
||||
} else {
|
||||
// literal
|
||||
strm.put(C);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// print two charactor escape
|
||||
strm.put(quote);
|
||||
strm.put(next);
|
||||
}
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
}}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*factory.h*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef FACTORY_H
|
||||
#define FACTORY_H
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
enum DebugLevel{noDebug,lowDebug,highDebug};
|
||||
|
||||
}}
|
||||
#endif /*FACTORY_H */
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVDATA_SRC)/json
|
||||
|
||||
INC += pv/json.h
|
||||
|
||||
LIBSRCS += parsehelper.cpp
|
||||
LIBSRCS += parseany.cpp
|
||||
LIBSRCS += parseinto.cpp
|
||||
LIBSRCS += print.cpp
|
||||
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
using pvd::yajl::integer_arg;
|
||||
using pvd::yajl::size_arg;
|
||||
|
||||
namespace {
|
||||
|
||||
struct context {
|
||||
|
||||
unsigned depth;
|
||||
|
||||
enum state_t {
|
||||
Undefined,
|
||||
Key,
|
||||
Array,
|
||||
} state;
|
||||
|
||||
pvd::shared_vector<void> arr;
|
||||
|
||||
pvd::ValueBuilder root,
|
||||
*cur;
|
||||
|
||||
std::string msg,
|
||||
key;
|
||||
|
||||
context() :depth(0u), state(Undefined), cur(&root) {}
|
||||
};
|
||||
|
||||
#define TRY context *self = (context*)ctx; try
|
||||
|
||||
#define CATCH() catch(std::exception& e) { self->msg = e.what(); return 0; }
|
||||
|
||||
int jtree_null(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
self->msg = "NULL value not permitted";
|
||||
return 0;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_boolean(void * ctx, int boolVal)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvBoolean>(self->key, boolVal);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvBoolean)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<pvd::boolean> arr(pvd::static_shared_vector_cast<pvd::boolean>(self->arr));
|
||||
arr.push_back(boolVal);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("boolean in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_integer(void * ctx, integer_arg integerVal)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvLong>(self->key, integerVal);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvLong)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<pvd::int64> arr(pvd::static_shared_vector_cast<pvd::int64>(self->arr));
|
||||
arr.push_back(integerVal);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("int64 in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_double(void * ctx, double doubleVal)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvDouble>(self->key, doubleVal);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvDouble)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<double> arr(pvd::static_shared_vector_cast<double>(self->arr));
|
||||
arr.push_back(doubleVal);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("double in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_string(void * ctx, const unsigned char * stringVal,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
std::string sval((const char*)stringVal, stringLen);
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvString>(self->key, sval);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvString)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<std::string> arr(pvd::static_shared_vector_cast<std::string>(self->arr));
|
||||
arr.push_back(sval);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("double in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth>0) {
|
||||
if(self->key.empty())
|
||||
throw std::logic_error("anonymous dict not top level?");
|
||||
self->cur = &self->cur->addNested(self->key);
|
||||
self->key.clear();
|
||||
}
|
||||
self->depth++;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_map_key(void * ctx, const unsigned char * key,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
if(!self->key.empty())
|
||||
throw std::logic_error("double key?");
|
||||
if(stringLen==0)
|
||||
throw std::runtime_error("empty key not allowed");
|
||||
self->key = std::string((const char*)key, stringLen);
|
||||
self->state = context::Key;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_end_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth>1)
|
||||
self->cur = &self->cur->endNested();
|
||||
else if(self->depth==0)
|
||||
throw std::logic_error("Unbalenced dict");
|
||||
self->depth--;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare array not supported");
|
||||
if(self->state!=context::Key)
|
||||
throw std::logic_error("bare array not supported");
|
||||
self->state = context::Array;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
int jtree_end_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->state!=context::Array)
|
||||
throw std::logic_error("Bad array parse");
|
||||
self->cur = &self->cur->add(self->key, pvd::freeze(self->arr));
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
|
||||
yajl_callbacks jtree_cbs = {
|
||||
&jtree_null,
|
||||
&jtree_boolean,
|
||||
&jtree_integer,
|
||||
&jtree_double,
|
||||
NULL, // number
|
||||
&jtree_string,
|
||||
&jtree_start_map,
|
||||
&jtree_map_key,
|
||||
&jtree_end_map,
|
||||
&jtree_start_array,
|
||||
&jtree_end_array,
|
||||
};
|
||||
|
||||
struct handler {
|
||||
yajl_handle handle;
|
||||
handler(yajl_handle handle) :handle(handle)
|
||||
{
|
||||
if(!handle)
|
||||
throw std::runtime_error("Failed to allocate yajl handle");
|
||||
}
|
||||
~handler() {
|
||||
yajl_free(handle);
|
||||
}
|
||||
operator yajl_handle() { return handle; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
epics::pvData::PVStructure::shared_pointer
|
||||
parseJSON(std::istream& strm)
|
||||
{
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
yajl_parser_config conf;
|
||||
memset(&conf, 0, sizeof(conf));
|
||||
conf.allowComments = 1;
|
||||
conf.checkUTF8 = 1;
|
||||
#endif
|
||||
|
||||
context ctxt;
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
|
||||
#else
|
||||
handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
|
||||
|
||||
yajl_config(handle, yajl_allow_comments, 1);
|
||||
#endif
|
||||
|
||||
if(!yajl_parse_helper(strm, handle))
|
||||
throw std::runtime_error(ctxt.msg);
|
||||
|
||||
return ctxt.cur->buildPVStructure();
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void check_trailing(const std::string& line)
|
||||
{
|
||||
size_t idx = line.find_first_not_of(" \t\n\r");
|
||||
if(idx==line.npos) return;
|
||||
// TODO: detect the end of potentially multi-line comments...
|
||||
// for now trailing comments not allowed
|
||||
throw std::runtime_error("Trailing junk");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
bool yajl_parse_helper(std::istream& src,
|
||||
yajl_handle handle)
|
||||
{
|
||||
unsigned linenum=0;
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
bool done = false;
|
||||
#endif
|
||||
|
||||
std::string line;
|
||||
while(std::getline(src, line)) {
|
||||
linenum++;
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
if(done) {
|
||||
check_trailing(line);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
yajl_status sts = yajl_parse(handle, (const unsigned char*)line.c_str(), line.size());
|
||||
|
||||
switch(sts) {
|
||||
case yajl_status_ok: {
|
||||
size_t consumed = yajl_get_bytes_consumed(handle);
|
||||
|
||||
if(consumed<line.size()) {
|
||||
check_trailing(line.substr(consumed));
|
||||
}
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
done = true;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case yajl_status_client_canceled:
|
||||
return false;
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
case yajl_status_insufficient_data:
|
||||
// continue with next line
|
||||
break;
|
||||
#endif
|
||||
case yajl_status_error:
|
||||
{
|
||||
std::ostringstream msg;
|
||||
unsigned char *raw = yajl_get_error(handle, 1, (const unsigned char*)line.c_str(), line.size());
|
||||
if(!raw) {
|
||||
msg<<"Unknown error on line "<<linenum;
|
||||
} else {
|
||||
try {
|
||||
msg<<"Error on line "<<linenum<<" : "<<(const char*)raw;
|
||||
}catch(...){
|
||||
yajl_free_error(handle, raw);
|
||||
throw;
|
||||
}
|
||||
yajl_free_error(handle, raw);
|
||||
}
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!src.eof() || src.bad()) {
|
||||
std::ostringstream msg;
|
||||
msg<<"I/O error after line "<<linenum;
|
||||
throw std::runtime_error(msg.str());
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
} else if(!done) {
|
||||
switch(yajl_parse_complete(handle)) {
|
||||
#else
|
||||
} else {
|
||||
switch(yajl_complete_parse(handle)) {
|
||||
#endif
|
||||
case yajl_status_ok:
|
||||
break;
|
||||
case yajl_status_client_canceled:
|
||||
return false;
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
case yajl_status_insufficient_data:
|
||||
throw std::runtime_error("unexpected end of input");
|
||||
#endif
|
||||
case yajl_status_error:
|
||||
throw std::runtime_error("Error while completing parsing");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
@@ -1,352 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
using pvd::yajl::integer_arg;
|
||||
using pvd::yajl::size_arg;
|
||||
|
||||
namespace {
|
||||
struct context {
|
||||
|
||||
std::string msg;
|
||||
|
||||
struct frame {
|
||||
pvd::PVFieldPtr fld;
|
||||
pvd::BitSet *assigned;
|
||||
frame(const pvd::PVFieldPtr& fld, pvd::BitSet *assigned)
|
||||
:fld(fld), assigned(assigned)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::vector<frame> stack_t;
|
||||
stack_t stack;
|
||||
|
||||
context(const pvd::PVFieldPtr& root, pvd::BitSet *assigned)
|
||||
{
|
||||
stack.push_back(frame(root, assigned));
|
||||
}
|
||||
};
|
||||
|
||||
#define TRY context *self = (context*)ctx; assert(!self->stack.empty()); try
|
||||
|
||||
#define CATCH() catch(std::exception& e) { if(self->msg.empty()) self->msg = e.what(); return 0; }
|
||||
|
||||
int jtree_null(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
self->msg = "NULL value not permitted";
|
||||
return 0;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
template<typename PVScalarT, typename PVArrayT>
|
||||
void valueAssign(context *self, typename PVScalarT::value_type val)
|
||||
{
|
||||
assert(!self->stack.empty());
|
||||
context::frame& back = self->stack.back();
|
||||
pvd::Type type(back.fld->getField()->getType());
|
||||
if(type==pvd::scalar) {
|
||||
pvd::PVScalar* fld(static_cast<pvd::PVScalar*>(back.fld.get()));
|
||||
|
||||
fld->putFrom(val);
|
||||
if(back.assigned)
|
||||
back.assigned->set(fld->getFieldOffset());
|
||||
self->stack.pop_back();
|
||||
// structure at the top of the stack
|
||||
|
||||
} else if(type==pvd::scalarArray) {
|
||||
pvd::PVScalarArray *fld(static_cast<pvd::PVScalarArray*>(back.fld.get()));
|
||||
|
||||
pvd::shared_vector<const void> carr;
|
||||
fld->getAs(carr);
|
||||
|
||||
switch(carr.original_type())
|
||||
{
|
||||
#define CASE_STRING
|
||||
#define CASE_REAL_INT64
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case epics::pvData::pv##PVACODE: { \
|
||||
pvd::shared_vector<const PVATYPE> arr(pvd::static_shared_vector_cast<const PVATYPE>(carr)); \
|
||||
pvd::shared_vector<PVATYPE> tarr(pvd::thaw(arr)); \
|
||||
tarr.push_back(pvd::castUnsafe<PVATYPE>(val)); \
|
||||
carr = pvd::static_shared_vector_cast<const void>(pvd::freeze(tarr)); \
|
||||
} break;
|
||||
#include <pv/typemap.h>
|
||||
#undef CASE
|
||||
#undef CASE_REAL_INT64
|
||||
#undef CASE_STRING
|
||||
}
|
||||
|
||||
fld->putFrom(carr);
|
||||
|
||||
// leave array field at top of stack
|
||||
|
||||
} else if(type==pvd::union_) {
|
||||
pvd::PVUnion* fld(static_cast<pvd::PVUnion*>(back.fld.get()));
|
||||
pvd::UnionConstPtr utype(fld->getUnion());
|
||||
|
||||
if(utype->isVariant()) {
|
||||
typename PVScalarT::shared_pointer elem(pvd::getPVDataCreate()->createPVScalar<PVScalarT>());
|
||||
|
||||
elem->put(val);
|
||||
|
||||
fld->set(elem);
|
||||
|
||||
} else {
|
||||
// attempt automagic assignment
|
||||
|
||||
const pvd::StringArray& names = utype->getFieldNames();
|
||||
const pvd::FieldConstPtrArray types = utype->getFields();
|
||||
assert(names.size()==types.size());
|
||||
|
||||
bool assigned = false;
|
||||
for(size_t i=0, N=names.size(); i<N; i++) {
|
||||
if(types[i]->getType()!=pvd::scalar) continue;
|
||||
|
||||
pvd::PVScalarPtr ufld(fld->select<pvd::PVScalar>(i));
|
||||
try{
|
||||
ufld->putFrom(val);
|
||||
assigned = true;
|
||||
}catch(std::runtime_error&){
|
||||
if(i==N-1)
|
||||
throw;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
if(!assigned)
|
||||
throw std::runtime_error("Unable to select union member");
|
||||
}
|
||||
if(back.assigned)
|
||||
back.assigned->set(fld->getFieldOffset());
|
||||
self->stack.pop_back();
|
||||
// structure back at the top of the stack
|
||||
|
||||
} else {
|
||||
throw std::invalid_argument("Can't assign value");
|
||||
}
|
||||
}
|
||||
|
||||
int jtree_boolean(void * ctx, int boolVal)
|
||||
{
|
||||
TRY {
|
||||
valueAssign<pvd::PVBoolean, pvd::PVBooleanArray>(self, !!boolVal);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_integer(void * ctx, integer_arg integerVal)
|
||||
{
|
||||
TRY {
|
||||
valueAssign<pvd::PVLong, pvd::PVLongArray>(self, integerVal);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_double(void * ctx, double doubleVal)
|
||||
{
|
||||
TRY {
|
||||
valueAssign<pvd::PVDouble, pvd::PVDoubleArray>(self, doubleVal);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_string(void * ctx, const unsigned char * stringVal,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
std::string val((const char*)stringVal, stringLen);
|
||||
valueAssign<pvd::PVString, pvd::PVStringArray>(self, val);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
|
||||
context::frame& back = self->stack.back();
|
||||
pvd::Type type = back.fld->getField()->getType();
|
||||
if(type==pvd::structure) {
|
||||
// will fill in
|
||||
} else if(type==pvd::structureArray) {
|
||||
// starting new element in structure array
|
||||
pvd::PVStructureArray* sarr(static_cast<pvd::PVStructureArray*>(back.fld.get()));
|
||||
|
||||
pvd::PVStructurePtr elem(pvd::getPVDataCreate()->createPVStructure(sarr->getStructureArray()->getStructure()));
|
||||
|
||||
self->stack.push_back(context::frame(elem, 0));
|
||||
} else {
|
||||
throw std::runtime_error("Can't map (sub)structure");
|
||||
}
|
||||
|
||||
assert(self->stack.back().fld->getField()->getType()==pvd::structure);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_map_key(void * ctx, const unsigned char * key,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
std::string name((const char*)key, stringLen);
|
||||
|
||||
// start_map() ensures we have a structure at the top of the stack
|
||||
pvd::PVStructure *fld = static_cast<pvd::PVStructure*>(self->stack.back().fld.get());
|
||||
|
||||
try {
|
||||
self->stack.push_back(context::frame(fld->getSubFieldT(name), self->stack.back().assigned));
|
||||
}catch(std::runtime_error& e){
|
||||
std::ostringstream strm;
|
||||
strm<<"At "<<fld->getFullName()<<" : "<<e.what()<<"\n";
|
||||
throw std::runtime_error(strm.str());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_end_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
assert(self->stack.back().fld->getField()->getType()==pvd::structure);
|
||||
|
||||
context::frame elem(self->stack.back());
|
||||
self->stack.pop_back();
|
||||
|
||||
if(!self->stack.empty() && self->stack.back().fld->getField()->getType()==pvd::structureArray) {
|
||||
// append element to struct array
|
||||
pvd::PVStructureArray *sarr = static_cast<pvd::PVStructureArray*>(self->stack.back().fld.get());
|
||||
|
||||
pvd::PVStructureArray::const_svector cval;
|
||||
sarr->swap(cval);
|
||||
|
||||
pvd::PVStructureArray::svector val(pvd::thaw(cval));
|
||||
|
||||
val.push_back(std::tr1::static_pointer_cast<pvd::PVStructure>(elem.fld));
|
||||
|
||||
sarr->replace(pvd::freeze(val));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
pvd::PVFieldPtr& back(self->stack.back().fld);
|
||||
pvd::Type type = back->getField()->getType();
|
||||
if(type!=pvd::structureArray && type!=pvd::scalarArray)
|
||||
throw std::runtime_error("Can't assign array");
|
||||
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
int jtree_end_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
|
||||
if(self->stack.back().assigned)
|
||||
self->stack.back().assigned->set(self->stack.back().fld->getFieldOffset());
|
||||
self->stack.pop_back();
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
|
||||
yajl_callbacks jtree_cbs = {
|
||||
&jtree_null,
|
||||
&jtree_boolean,
|
||||
&jtree_integer,
|
||||
&jtree_double,
|
||||
NULL, // number
|
||||
&jtree_string,
|
||||
&jtree_start_map,
|
||||
&jtree_map_key,
|
||||
&jtree_end_map,
|
||||
&jtree_start_array,
|
||||
&jtree_end_array,
|
||||
};
|
||||
|
||||
struct handler {
|
||||
yajl_handle handle;
|
||||
handler(yajl_handle handle) :handle(handle)
|
||||
{
|
||||
if(!handle)
|
||||
throw std::runtime_error("Failed to allocate yajl handle");
|
||||
}
|
||||
~handler() {
|
||||
yajl_free(handle);
|
||||
}
|
||||
operator yajl_handle() { return handle; }
|
||||
};
|
||||
|
||||
struct noop {
|
||||
void operator()(pvd::PVField*) {}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
epicsShareFunc
|
||||
void parseJSON(std::istream& strm,
|
||||
PVField& dest,
|
||||
BitSet *assigned)
|
||||
{
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
yajl_parser_config conf;
|
||||
memset(&conf, 0, sizeof(conf));
|
||||
conf.allowComments = 1;
|
||||
conf.checkUTF8 = 1;
|
||||
#endif
|
||||
|
||||
// we won't create refs to 'dest' which presist beyond this call.
|
||||
// however, it is convienent to treat 'dest' in the same manner as
|
||||
// any union/structureArray memebers it may contain.
|
||||
PVFieldPtr fakedest(&dest, noop());
|
||||
|
||||
context ctxt(fakedest, assigned);
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
|
||||
#else
|
||||
handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
|
||||
|
||||
yajl_config(handle, yajl_allow_comments, 1);
|
||||
#endif
|
||||
|
||||
|
||||
if(!yajl_parse_helper(strm, handle))
|
||||
throw std::runtime_error(ctxt.msg);
|
||||
|
||||
if(!ctxt.stack.empty())
|
||||
throw std::logic_error("field stack not empty");
|
||||
assert(fakedest.use_count()==1);
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
namespace {
|
||||
|
||||
struct args {
|
||||
std::ostream& strm;
|
||||
const pvd::JSONPrintOptions& opts;
|
||||
|
||||
unsigned indent;
|
||||
|
||||
args(std::ostream& strm,
|
||||
const pvd::JSONPrintOptions& opts)
|
||||
:strm(strm)
|
||||
,opts(opts)
|
||||
,indent(opts.indent)
|
||||
{}
|
||||
|
||||
void doIntent() {
|
||||
if(!opts.multiLine) return;
|
||||
strm.put('\n');
|
||||
unsigned i=indent;
|
||||
while(i--) strm.put(' ');
|
||||
}
|
||||
};
|
||||
|
||||
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask);
|
||||
|
||||
void show_struct(args& A, const pvd::PVStructure* fld, const pvd::BitSet *mask)
|
||||
{
|
||||
const pvd::StructureConstPtr& type = fld->getStructure();
|
||||
const pvd::PVFieldPtrArray& children = fld->getPVFields();
|
||||
|
||||
const pvd::StringArray& names = type->getFieldNames();
|
||||
|
||||
A.strm.put('{');
|
||||
A.indent++;
|
||||
|
||||
bool first = true;
|
||||
for(size_t i=0, N=names.size(); i<N; i++)
|
||||
{
|
||||
if(mask && !mask->get(children[i]->getFieldOffset())) continue;
|
||||
|
||||
if(first)
|
||||
first = false;
|
||||
else
|
||||
A.strm.put(',');
|
||||
A.doIntent();
|
||||
A.strm<<'\"'<<names[i]<<"\": ";
|
||||
show_field(A, children[i].get(), mask);
|
||||
}
|
||||
|
||||
A.indent--;
|
||||
A.doIntent();
|
||||
A.strm.put('}');
|
||||
}
|
||||
|
||||
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask)
|
||||
{
|
||||
switch(fld->getField()->getType())
|
||||
{
|
||||
case pvd::scalar:
|
||||
{
|
||||
const pvd::PVScalar *scalar=static_cast<const pvd::PVScalar*>(fld);
|
||||
if(scalar->getScalar()->getScalarType()==pvd::pvString) {
|
||||
A.strm<<'\"'<<scalar->getAs<std::string>()<<'\"';
|
||||
} else {
|
||||
A.strm<<scalar->getAs<std::string>();
|
||||
}
|
||||
}
|
||||
return;
|
||||
case pvd::scalarArray:
|
||||
{
|
||||
const pvd::PVScalarArray *scalar=static_cast<const pvd::PVScalarArray*>(fld);
|
||||
const bool isstring = scalar->getScalarArray()->getElementType()==pvd::pvString;
|
||||
|
||||
pvd::shared_vector<const void> arr;
|
||||
scalar->getAs<void>(arr);
|
||||
|
||||
pvd::shared_vector<const std::string> sarr(pvd::shared_vector_convert<const std::string>(arr));
|
||||
|
||||
A.strm.put('[');
|
||||
for(size_t i=0, N=sarr.size(); i<N; i++) {
|
||||
if(i!=0)
|
||||
A.strm.put(',');
|
||||
if(isstring)
|
||||
A.strm.put('\"');
|
||||
A.strm<<sarr[i];
|
||||
if(isstring)
|
||||
A.strm.put('\"');
|
||||
}
|
||||
A.strm.put(']');
|
||||
}
|
||||
return;
|
||||
case pvd::structure:
|
||||
show_struct(A, static_cast<const pvd::PVStructure*>(fld), mask);
|
||||
return;
|
||||
case pvd::structureArray:
|
||||
{
|
||||
pvd::PVStructureArray::const_svector arr(static_cast<const pvd::PVStructureArray*>(fld)->view());
|
||||
A.strm.put('[');
|
||||
A.indent++;
|
||||
|
||||
for(size_t i=0, N=arr.size(); i<N; i++) {
|
||||
if(i!=0)
|
||||
A.strm.put(',');
|
||||
A.doIntent();
|
||||
if(arr[i])
|
||||
show_struct(A, arr[i].get(), 0);
|
||||
else
|
||||
A.strm<<"NULL";
|
||||
}
|
||||
|
||||
A.indent--;
|
||||
A.doIntent();
|
||||
A.strm.put(']');
|
||||
}
|
||||
return;
|
||||
case pvd::union_:
|
||||
{
|
||||
const pvd::PVUnion *U=static_cast<const pvd::PVUnion*>(fld);
|
||||
const pvd::PVField::const_shared_pointer& C(U->get());
|
||||
|
||||
if(!C) {
|
||||
A.strm<<"null";
|
||||
} else {
|
||||
show_field(A, C.get(), 0);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case pvd::unionArray: {
|
||||
const pvd::PVUnionArray *U=static_cast<const pvd::PVUnionArray*>(fld);
|
||||
pvd::PVUnionArray::const_svector arr(U->view());
|
||||
A.strm.put('[');
|
||||
A.indent++;
|
||||
|
||||
for(size_t i=0, N=arr.size(); i<N; i++) {
|
||||
if(i!=0)
|
||||
A.strm.put(',');
|
||||
A.doIntent();
|
||||
if(arr[i])
|
||||
show_field(A, arr[i].get(), 0);
|
||||
else
|
||||
A.strm<<"NULL";
|
||||
}
|
||||
|
||||
A.indent--;
|
||||
A.doIntent();
|
||||
A.strm.put(']');
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
// should not be reached
|
||||
if(A.opts.ignoreUnprintable)
|
||||
A.strm<<"// unprintable field type";
|
||||
else
|
||||
throw std::runtime_error("Encountered unprintable field type");
|
||||
}
|
||||
|
||||
void expandBS(const pvd::PVStructure& top, pvd::BitSet& mask, bool parents) {
|
||||
if(mask.get(0)) { // special handling because getSubField(0) not allowed
|
||||
// wildcard
|
||||
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
|
||||
mask.set(idx);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(pvd::int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
|
||||
pvd::PVField::const_shared_pointer fld = top.getSubFieldT(idx);
|
||||
|
||||
// look forward and mark all children
|
||||
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
|
||||
mask.set(i);
|
||||
|
||||
if(parents) {
|
||||
// look back and mark all parents
|
||||
// we've already stepped past all parents so siblings will not be automatically marked
|
||||
for(const pvd::PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
|
||||
mask.set(parent->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
JSONPrintOptions::JSONPrintOptions()
|
||||
:multiLine(true)
|
||||
,ignoreUnprintable(true)
|
||||
,indent(0)
|
||||
{}
|
||||
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVStructure& val,
|
||||
const BitSet& mask,
|
||||
const JSONPrintOptions& opts)
|
||||
{
|
||||
args A(strm, opts);
|
||||
pvd::BitSet emask(mask);
|
||||
expandBS(val, emask, true);
|
||||
if(!emask.get(0)) return;
|
||||
show_struct(A, &val, &emask);
|
||||
}
|
||||
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField& val,
|
||||
const JSONPrintOptions& opts)
|
||||
{
|
||||
args A(strm, opts);
|
||||
show_field(A, &val, 0);
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
#ifndef PV_JSON_H
|
||||
#define PV_JSON_H
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define pvjson_epicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <yajl_parse.h>
|
||||
|
||||
#ifdef pvjson_epicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# include "shareLib.h"
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
class BitSet;
|
||||
|
||||
/** @defgroup pvjson JSON print/parse
|
||||
*
|
||||
* Printing PVField as JSON and parsing JSON into PVField.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
//! Options used during printing
|
||||
struct epicsShareClass JSONPrintOptions
|
||||
{
|
||||
bool multiLine; //!< include new lines
|
||||
bool ignoreUnprintable;//!< ignore union/union array when encountered
|
||||
unsigned indent; //!< Initial indentation (# of spaces)
|
||||
JSONPrintOptions();
|
||||
};
|
||||
|
||||
/** Print PVStructure as JSON
|
||||
*
|
||||
* 'mask' selects those fields which will be printed.
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVStructure& val,
|
||||
const BitSet& mask,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions());
|
||||
|
||||
/** Print PVField as JSON
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField& val,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions());
|
||||
|
||||
// To be deprecated in favor of previous form
|
||||
FORCE_INLINE
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField::const_shared_pointer& val,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions())
|
||||
{
|
||||
printJSON(strm, *val, opts);
|
||||
}
|
||||
|
||||
/** Parse JSON text into a PVStructure
|
||||
*
|
||||
* Restrictions:
|
||||
*
|
||||
* - Top level must be {} dict/object
|
||||
* - field values must be number, string, array, or dict/object
|
||||
* - array values must be number or string
|
||||
*/
|
||||
epicsShareFunc
|
||||
PVStructure::shared_pointer parseJSON(std::istream& strm);
|
||||
|
||||
/** Parse JSON and store into the provided PVStructure.
|
||||
*
|
||||
* Restrictions:
|
||||
*
|
||||
* - array of union not supported
|
||||
* - Only scalar value assigned to union
|
||||
*
|
||||
* @param strm Read JSON text from stream
|
||||
* @param dest Store in fields of this structure
|
||||
* @param assigned Which fields of _dest_ were assigned. (Optional)
|
||||
* @throws std::runtime_error on failure. dest and assigned may be modified.
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void parseJSON(std::istream& strm,
|
||||
PVField& dest,
|
||||
BitSet *assigned=0);
|
||||
|
||||
// To be deprecated in favor of previous form
|
||||
FORCE_INLINE
|
||||
void parseJSON(std::istream& strm,
|
||||
const PVField::shared_pointer& dest,
|
||||
BitSet *assigned=0)
|
||||
{
|
||||
parseJSON(strm, *dest, assigned);
|
||||
}
|
||||
|
||||
|
||||
/** Wrapper around yajl_parse()
|
||||
*
|
||||
* Parse entire input stream.
|
||||
* Errors if extranious non-whitespace found after the point were parsing completes.
|
||||
*
|
||||
* @param src The stream from which input charactors are read
|
||||
* @param handle A parser handle previously allocated with yajl_alloc(). Not free'd on success or failure.
|
||||
*
|
||||
* @returns true if parsing completes successfully. false if parsing cancelled by callback. throws other errors
|
||||
*
|
||||
* @note The form of this call depends on EPICS_YAJL_VERSION
|
||||
*/
|
||||
epicsShareFunc
|
||||
bool yajl_parse_helper(std::istream& src,
|
||||
yajl_handle handle);
|
||||
|
||||
namespace yajl {
|
||||
// undef implies API version 0
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
typedef long integer_arg;
|
||||
typedef unsigned size_arg;
|
||||
#else
|
||||
typedef long long integer_arg;
|
||||
typedef size_t size_arg;
|
||||
#endif
|
||||
} // namespace epics::pvData::yajl
|
||||
|
||||
/** @} */
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#else
|
||||
# error JSON parser requires EPICS Base >= 3.15.0.1
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
#endif // PV_JSON_H
|
||||
@@ -2,40 +2,42 @@
|
||||
|
||||
SRC_DIRS += $(PVDATA_SRC)/misc
|
||||
|
||||
INC += pv/noDefaultMethods.h
|
||||
INC += pv/lock.h
|
||||
INC += pv/serialize.h
|
||||
INC += pv/bitSet.h
|
||||
INC += pv/byteBuffer.h
|
||||
INC += pv/epicsException.h
|
||||
INC += pv/serializeHelper.h
|
||||
INC += pv/event.h
|
||||
INC += pv/thread.h
|
||||
INC += pv/timer.h
|
||||
INC += pv/status.h
|
||||
INC += pv/sharedPtr.h
|
||||
INC += pv/debugPtr.h
|
||||
INC += pv/localStaticLock.h
|
||||
INC += pv/typeCast.h
|
||||
INC += pv/sharedVector.h
|
||||
INC += pv/templateMeta.h
|
||||
INC += pv/current_function.h
|
||||
INC += pv/pvUnitTest.h
|
||||
INC += pv/reftrack.h
|
||||
INC += pv/anyscalar.h
|
||||
INC += noDefaultMethods.h
|
||||
INC += lock.h
|
||||
INC += requester.h
|
||||
INC += serialize.h
|
||||
INC += bitSet.h
|
||||
INC += byteBuffer.h
|
||||
INC += epicsException.h
|
||||
INC += serializeHelper.h
|
||||
INC += event.h
|
||||
INC += thread.h
|
||||
INC += executor.h
|
||||
INC += timeFunction.h
|
||||
INC += timer.h
|
||||
INC += queue.h
|
||||
INC += messageQueue.h
|
||||
INC += destroyable.h
|
||||
INC += status.h
|
||||
INC += sharedPtr.h
|
||||
INC += localStaticLock.h
|
||||
INC += typeCast.h
|
||||
INC += sharedVector.h
|
||||
INC += templateMeta.h
|
||||
INC += current_function.h
|
||||
|
||||
LIBSRCS += byteBuffer.cpp
|
||||
LIBSRCS += bitSet.cpp
|
||||
LIBSRCS += epicsException.cpp
|
||||
LIBSRCS += requester.cpp
|
||||
LIBSRCS += serializeHelper.cpp
|
||||
LIBSRCS += event.cpp
|
||||
LIBSRCS += executor.cpp
|
||||
LIBSRCS += timeFunction.cpp
|
||||
LIBSRCS += timer.cpp
|
||||
LIBSRCS += status.cpp
|
||||
LIBSRCS += messageQueue.cpp
|
||||
LIBSRCS += localStaticLock.cpp
|
||||
LIBSRCS += typeCast.cpp
|
||||
LIBSRCS += thread.cpp
|
||||
LIBSRCS += parseToPOD.cpp
|
||||
LIBSRCS += pvUnitTest.cpp
|
||||
LIBSRCS += debugPtr.cpp
|
||||
LIBSRCS += reftrack.cpp
|
||||
LIBSRCS += anyscalar.cpp
|
||||
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
|
||||
#include "pv/anyscalar.h"
|
||||
|
||||
namespace epics {namespace pvData {
|
||||
|
||||
AnyScalar::AnyScalar(ScalarType type, const void *buf)
|
||||
{
|
||||
if(type==pvString) {
|
||||
new (_wrap.blob) std::string(*static_cast<const std::string*>(buf));
|
||||
|
||||
} else {
|
||||
memcpy(_wrap.blob, buf, ScalarTypeFunc::elementSize(type));
|
||||
}
|
||||
|
||||
_stype = type;
|
||||
}
|
||||
|
||||
AnyScalar::AnyScalar(const AnyScalar& o)
|
||||
:_stype(o._stype)
|
||||
{
|
||||
if(o._stype==pvString) {
|
||||
new (_wrap.blob) std::string(o._as<std::string>());
|
||||
} else if(o._stype!=(ScalarType)-1) {
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
}
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar::AnyScalar(AnyScalar&& o)
|
||||
:_stype(o._stype)
|
||||
{
|
||||
typedef std::string string;
|
||||
if(o._stype==pvString) {
|
||||
new (_wrap.blob) std::string();
|
||||
_as<std::string>() = std::move(o._as<std::string>());
|
||||
o._as<string>().~string();
|
||||
} else if(o._stype!=(ScalarType)-1) {
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
}
|
||||
o._stype = (ScalarType)-1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void AnyScalar::clear() {
|
||||
if(_stype==pvString) {
|
||||
typedef std::string string;
|
||||
_as<string>().~string();
|
||||
}
|
||||
// other types need no cleanup
|
||||
_stype = (ScalarType)-1;
|
||||
}
|
||||
|
||||
void AnyScalar::swap(AnyScalar& o) {
|
||||
typedef std::string string;
|
||||
switch((int)_stype) {
|
||||
case -1:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// nil <-> nil
|
||||
break;
|
||||
case pvString:
|
||||
// nil <-> string
|
||||
new (_wrap.blob) std::string();
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
o._as<std::string>().~string();
|
||||
break;
|
||||
default:
|
||||
// nil <-> non-string
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case pvString:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// string <-> nil
|
||||
new (o._wrap.blob) std::string();
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
_as<std::string>().~string();
|
||||
break;
|
||||
case pvString:
|
||||
// string <-> string
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
break;
|
||||
default: {
|
||||
// string <-> non-string
|
||||
std::string temp;
|
||||
temp.swap(_as<std::string>());
|
||||
|
||||
_as<std::string>().~string();
|
||||
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
|
||||
new (o._wrap.blob) std::string();
|
||||
temp.swap(o._as<std::string>());
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// non-string <-> nil
|
||||
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
|
||||
break;
|
||||
case pvString: {
|
||||
// non-string <-> string
|
||||
std::string temp;
|
||||
temp.swap(o._as<std::string>());
|
||||
|
||||
o._as<std::string>().~string();
|
||||
|
||||
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
|
||||
|
||||
new (_wrap.blob) std::string();
|
||||
temp.swap(_as<std::string>());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// non-string <-> non-string
|
||||
_largest_blob temp;
|
||||
memcpy(&temp, _wrap.blob, sizeof(_largest_blob));
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
memcpy(o._wrap.blob, &temp, sizeof(_largest_blob));
|
||||
// std::swap(o._wrap.blob, _wrap.blob); // gcc <=4.3 doesn't like this
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
std::swap(_stype, o._stype);
|
||||
}
|
||||
const void* AnyScalar::bufferUnsafe() const {
|
||||
if(_stype==pvString) {
|
||||
return as<std::string>().c_str();
|
||||
} else {
|
||||
return _wrap.blob;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v)
|
||||
{
|
||||
switch(v.type()) {
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pv ## PVACODE: strm<<v._as<PVATYPE>(); break;
|
||||
#define CASE_REAL_INT64
|
||||
#define CASE_STRING
|
||||
#include "pv/typemap.h"
|
||||
#undef CASE
|
||||
#undef CASE_REAL_INT64
|
||||
#undef CASE_STRING
|
||||
default:
|
||||
strm<<"(nil)"; break;
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
@@ -1,7 +1,8 @@
|
||||
/* bitSet.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mes
|
||||
@@ -9,38 +10,12 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
/*
|
||||
* BitSets are packed into arrays of "words." Currently a word is
|
||||
* a long, which consists of 64 bits, requiring 6 address bits.
|
||||
* The choice of word size is determined purely by performance concerns.
|
||||
*/
|
||||
#define ADDRESS_BITS_PER_WORD 6u
|
||||
#define BITS_PER_WORD (1u << ADDRESS_BITS_PER_WORD)
|
||||
#define BYTES_PER_WORD sizeof(uint64)
|
||||
#define BIT_INDEX_MASK (BITS_PER_WORD - 1u)
|
||||
|
||||
/** Used to shift left or right for a partial word mask */
|
||||
#define WORD_MASK ~((uint64)0)
|
||||
|
||||
// index of work containing this bit
|
||||
#define WORD_INDEX(bitn) ((bitn)>>ADDRESS_BITS_PER_WORD)
|
||||
// bit offset within word
|
||||
#define WORD_OFFSET(bitn) ((bitn)&BIT_INDEX_MASK)
|
||||
|
||||
// the words vector should be size()d as small as posible,
|
||||
// so the last word should always have a bit set when the set is not empty
|
||||
#define CHECK_POST() assert(words.empty() || words.back()!=0)
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
BitSet::shared_pointer BitSet::create(uint32 nbits)
|
||||
@@ -48,76 +23,90 @@ namespace epics { namespace pvData {
|
||||
return BitSet::shared_pointer(new BitSet(nbits));
|
||||
}
|
||||
|
||||
BitSet::BitSet() {}
|
||||
BitSet::BitSet() : words(0), wordsLength(0), wordsInUse(0) {
|
||||
initWords(BITS_PER_WORD);
|
||||
|
||||
BitSet::BitSet(uint32 nbits)
|
||||
{
|
||||
words.reserve((nbits == 0) ? 1 : WORD_INDEX(nbits-1) + 1);
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
BitSet::BitSet(std::initializer_list<uint32> I)
|
||||
{
|
||||
// optimistically guess that highest bit is last (not required)
|
||||
words.reserve((I.size() == 0) ? 1 : WORD_INDEX(*(I.end()-1)) + 1);
|
||||
BitSet::BitSet(uint32 nbits) : words(0), wordsLength(0), wordsInUse(0) {
|
||||
initWords(nbits);
|
||||
|
||||
for(uint32 idx : I)
|
||||
{
|
||||
set(idx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
BitSet::~BitSet() {}
|
||||
BitSet::~BitSet() {
|
||||
delete[] words;
|
||||
}
|
||||
|
||||
void BitSet::initWords(uint32 nbits) {
|
||||
uint32 length = (nbits <= 0) ? 1 : wordIndex(nbits-1) + 1;
|
||||
if (words) delete[] words;
|
||||
words = new uint64[length];
|
||||
memset(words, 0, sizeof(uint64)*length);
|
||||
wordsLength = length;
|
||||
}
|
||||
|
||||
void BitSet::recalculateWordsInUse() {
|
||||
// step back from the end to find the first non-zero element
|
||||
size_t nsize = words.size();
|
||||
for(; nsize; nsize--) {
|
||||
if(words[nsize-1])
|
||||
// wordsInUse is unsigned
|
||||
if (wordsInUse == 0)
|
||||
return;
|
||||
|
||||
// Traverse the bitset until a used word is found
|
||||
int32 i;
|
||||
for (i = (int32)wordsInUse-1; i >= 0; i--)
|
||||
if (words[i] != 0)
|
||||
break;
|
||||
}
|
||||
words.resize(nsize);
|
||||
CHECK_POST();
|
||||
|
||||
wordsInUse = i+1; // The new logical size
|
||||
}
|
||||
|
||||
void BitSet::ensureCapacity(uint32 wordsRequired) {
|
||||
words.resize(std::max(words.size(), (size_t)wordsRequired), 0);
|
||||
if (wordsLength < wordsRequired) {
|
||||
|
||||
// create and copy
|
||||
uint64* newwords = new uint64[wordsRequired];
|
||||
memset(newwords, 0, sizeof(uint64)*wordsRequired);
|
||||
memcpy(newwords, words, sizeof(uint64)*wordsLength);
|
||||
if (words) delete[] words;
|
||||
words = newwords;
|
||||
wordsLength = wordsRequired;
|
||||
}
|
||||
}
|
||||
|
||||
void BitSet::expandTo(uint32 wordIndex) {
|
||||
ensureCapacity(wordIndex+1);
|
||||
uint32 wordsRequired = wordIndex+1;
|
||||
if (wordsInUse < wordsRequired) {
|
||||
ensureCapacity(wordsRequired);
|
||||
wordsInUse = wordsRequired;
|
||||
}
|
||||
}
|
||||
|
||||
BitSet& BitSet::flip(uint32 bitIndex) {
|
||||
void BitSet::flip(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
|
||||
words[wordIdx] ^= (((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
words[wordIdx] ^= (((uint64)1) << (bitIndex % BITS_PER_WORD));
|
||||
|
||||
recalculateWordsInUse();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::set(uint32 bitIndex) {
|
||||
void BitSet::set(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
|
||||
words[wordIdx] |= (((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
return *this;
|
||||
words[wordIdx] |= (((uint64)1) << (bitIndex % BITS_PER_WORD));
|
||||
}
|
||||
|
||||
BitSet& BitSet::clear(uint32 bitIndex) {
|
||||
void BitSet::clear(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
if (wordIdx < words.size()) {
|
||||
words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
if (wordIdx >= wordsInUse)
|
||||
return;
|
||||
|
||||
recalculateWordsInUse();
|
||||
}
|
||||
return *this;
|
||||
words[wordIdx] &= ~(((uint64)1) << (bitIndex % BITS_PER_WORD));
|
||||
|
||||
recalculateWordsInUse();
|
||||
}
|
||||
|
||||
void BitSet::set(uint32 bitIndex, bool value) {
|
||||
@@ -128,13 +117,14 @@ namespace epics { namespace pvData {
|
||||
}
|
||||
|
||||
bool BitSet::get(uint32 bitIndex) const {
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
return ((wordIdx < words.size())
|
||||
&& ((words[wordIdx] & (((uint64)1) << WORD_OFFSET(bitIndex))) != 0));
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
return ((wordIdx < wordsInUse)
|
||||
&& ((words[wordIdx] & (((uint64)1) << (bitIndex % BITS_PER_WORD))) != 0));
|
||||
}
|
||||
|
||||
void BitSet::clear() {
|
||||
words.clear();
|
||||
while (wordsInUse > 0)
|
||||
words[--wordsInUse] = 0;
|
||||
}
|
||||
|
||||
uint32 BitSet::numberOfTrailingZeros(uint64 i) {
|
||||
@@ -163,8 +153,8 @@ namespace epics { namespace pvData {
|
||||
|
||||
int32 BitSet::nextSetBit(uint32 fromIndex) const {
|
||||
|
||||
uint32 u = WORD_INDEX(fromIndex);
|
||||
if (u >= words.size())
|
||||
uint32 u = wordIndex(fromIndex);
|
||||
if (u >= wordsInUse)
|
||||
return -1;
|
||||
|
||||
uint64 word = words[u] & (WORD_MASK << (fromIndex % BITS_PER_WORD));
|
||||
@@ -172,7 +162,7 @@ namespace epics { namespace pvData {
|
||||
while (true) {
|
||||
if (word != 0)
|
||||
return (u * BITS_PER_WORD) + numberOfTrailingZeros(word);
|
||||
if (++u == words.size())
|
||||
if (++u == wordsInUse)
|
||||
return -1;
|
||||
word = words[u];
|
||||
}
|
||||
@@ -181,8 +171,8 @@ namespace epics { namespace pvData {
|
||||
int32 BitSet::nextClearBit(uint32 fromIndex) const {
|
||||
// Neither spec nor implementation handle bitsets of maximal length.
|
||||
|
||||
uint32 u = WORD_INDEX(fromIndex);
|
||||
if (u >= words.size())
|
||||
uint32 u = wordIndex(fromIndex);
|
||||
if (u >= wordsInUse)
|
||||
return fromIndex;
|
||||
|
||||
uint64 word = ~words[u] & (WORD_MASK << (fromIndex % BITS_PER_WORD));
|
||||
@@ -190,77 +180,79 @@ namespace epics { namespace pvData {
|
||||
while (true) {
|
||||
if (word != 0)
|
||||
return (u * BITS_PER_WORD) + numberOfTrailingZeros(word);
|
||||
if (++u == words.size())
|
||||
return words.size() * BITS_PER_WORD;
|
||||
if (++u == wordsInUse)
|
||||
return wordsInUse * BITS_PER_WORD;
|
||||
word = ~words[u];
|
||||
}
|
||||
}
|
||||
|
||||
bool BitSet::isEmpty() const {
|
||||
return words.empty();
|
||||
return (wordsInUse == 0);
|
||||
}
|
||||
|
||||
uint32 BitSet::cardinality() const {
|
||||
uint32 sum = 0;
|
||||
for (uint32 i = 0; i < words.size(); i++)
|
||||
for (uint32 i = 0; i < wordsInUse; i++)
|
||||
sum += bitCount(words[i]);
|
||||
return sum;
|
||||
}
|
||||
|
||||
uint32 BitSet::size() const {
|
||||
return words.size() * BITS_PER_WORD;
|
||||
}
|
||||
|
||||
bool BitSet::logical_and(const BitSet& set) const
|
||||
{
|
||||
size_t nwords = std::min(words.size(), set.words.size());
|
||||
for(size_t i=0; i<nwords; i++) {
|
||||
if(words[i] & set.words[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool BitSet::logical_or(const BitSet& set) const
|
||||
{
|
||||
return !words.empty() || !set.words.empty();
|
||||
return wordsLength * BITS_PER_WORD;
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator&=(const BitSet& set) {
|
||||
// Check for self-assignment!
|
||||
if (this == &set) return *this;
|
||||
|
||||
// the result length will be <= the shorter of the two inputs
|
||||
words.resize(std::min(words.size(), set.words.size()), 0);
|
||||
while (wordsInUse > set.wordsInUse)
|
||||
words[--wordsInUse] = 0;
|
||||
|
||||
for(size_t i=0, e=words.size(); i<e; i++)
|
||||
// Perform logical AND on words in common
|
||||
for (uint32 i = 0; i < wordsInUse; i++)
|
||||
words[i] &= set.words[i];
|
||||
|
||||
recalculateWordsInUse();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator|=(const BitSet& set) {
|
||||
// Check for self-assignment!
|
||||
if (this == &set) return *this;
|
||||
|
||||
// result length will be the same as the longer of the two inputs
|
||||
words.resize(std::max(words.size(), set.words.size()), 0);
|
||||
|
||||
// since we expand w/ zeros, then iterate using the size of the other vector
|
||||
for(size_t i=0, e=set.words.size(); i<e; i++)
|
||||
uint32 wordsInCommon = wordsInUse;
|
||||
if(wordsInUse>set.wordsInUse) wordsInCommon = set.wordsInUse;
|
||||
if (wordsInUse < set.wordsInUse) {
|
||||
ensureCapacity(set.wordsInUse);
|
||||
wordsInUse = set.wordsInUse;
|
||||
}
|
||||
// Perform logical OR on words in common
|
||||
for (uint32 i =0; i < wordsInCommon; i++) {
|
||||
words[i] |= set.words[i];
|
||||
|
||||
CHECK_POST();
|
||||
}
|
||||
// Copy any remaining words
|
||||
for(uint32 i=wordsInCommon; i<set.wordsInUse; ++i) {
|
||||
words[i] = set.words[i];
|
||||
}
|
||||
// recalculateWordsInUse() is not needed
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator^=(const BitSet& set) {
|
||||
// result length will <= the longer of the two inputs
|
||||
words.resize(std::max(words.size(), set.words.size()), 0);
|
||||
|
||||
for(size_t i=0, e=set.words.size(); i<e; i++)
|
||||
uint32 wordsInCommon = wordsInUse;
|
||||
if(wordsInUse>set.wordsInUse) wordsInCommon = set.wordsInUse;
|
||||
if (wordsInUse < set.wordsInUse) {
|
||||
ensureCapacity(set.wordsInUse);
|
||||
wordsInUse = set.wordsInUse;
|
||||
}
|
||||
// Perform logical OR on words in common
|
||||
for (uint32 i =0; i < wordsInCommon; i++) {
|
||||
words[i] ^= set.words[i];
|
||||
|
||||
}
|
||||
// Copy any remaining words
|
||||
for(uint32 i=wordsInCommon; i<set.wordsInUse; ++i) {
|
||||
words[i] = set.words[i];
|
||||
}
|
||||
recalculateWordsInUse();
|
||||
return *this;
|
||||
}
|
||||
@@ -268,27 +260,32 @@ namespace epics { namespace pvData {
|
||||
|
||||
BitSet& BitSet::operator=(const BitSet &set) {
|
||||
// Check for self-assignment!
|
||||
if (this != &set) {
|
||||
words = set.words;
|
||||
if (this == &set) return *this;
|
||||
|
||||
// we ensure that words array size is adequate (and not wordsInUse to ensure capacity to the future)
|
||||
if (wordsLength < set.wordsLength)
|
||||
{
|
||||
if (words) delete[] words;
|
||||
words = new uint64[set.wordsLength];
|
||||
wordsLength = set.wordsLength;
|
||||
}
|
||||
memcpy(words, set.words, sizeof(uint64)*set.wordsInUse);
|
||||
wordsInUse = set.wordsInUse;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BitSet::swap(BitSet& set)
|
||||
{
|
||||
words.swap(set.words);
|
||||
}
|
||||
|
||||
void BitSet::or_and(const BitSet& set1, const BitSet& set2) {
|
||||
uint32 inUse = (set1.wordsInUse < set2.wordsInUse) ? set1.wordsInUse : set2.wordsInUse;
|
||||
|
||||
const size_t andlen = std::min(set1.words.size(), set2.words.size());
|
||||
words.resize(std::max(words.size(), andlen), 0);
|
||||
ensureCapacity(inUse);
|
||||
wordsInUse = inUse;
|
||||
|
||||
// Perform logical AND on words in common
|
||||
for (uint32 i = 0; i < andlen; i++)
|
||||
for (uint32 i = 0; i < inUse; i++)
|
||||
words[i] |= (set1.words[i] & set2.words[i]);
|
||||
|
||||
recalculateWordsInUse();
|
||||
// recalculateWordsInUse()...
|
||||
}
|
||||
|
||||
bool BitSet::operator==(const BitSet &set) const
|
||||
@@ -296,11 +293,11 @@ namespace epics { namespace pvData {
|
||||
if (this == &set)
|
||||
return true;
|
||||
|
||||
if (words.size() != set.words.size())
|
||||
if (wordsInUse != set.wordsInUse)
|
||||
return false;
|
||||
|
||||
// Check words in use by both BitSets
|
||||
for (uint32 i = 0; i < words.size(); i++)
|
||||
for (uint32 i = 0; i < wordsInUse; i++)
|
||||
if (words[i] != set.words[i])
|
||||
return false;
|
||||
|
||||
@@ -313,55 +310,56 @@ namespace epics { namespace pvData {
|
||||
}
|
||||
|
||||
void BitSet::serialize(ByteBuffer* buffer, SerializableControl* flusher) const {
|
||||
|
||||
uint32 n = words.size();
|
||||
|
||||
uint32 n = wordsInUse;
|
||||
if (n == 0) {
|
||||
SerializeHelper::writeSize(0, buffer, flusher);
|
||||
return;
|
||||
}
|
||||
uint32 len = BYTES_PER_WORD * (n-1); // length excluding bits in the last word
|
||||
// count non-zero bytes in the last word
|
||||
uint32 len = 8 * (n-1);
|
||||
for (uint64 x = words[n - 1]; x != 0; x >>= 8)
|
||||
len++;
|
||||
|
||||
|
||||
SerializeHelper::writeSize(len, buffer, flusher);
|
||||
flusher->ensureBuffer(len);
|
||||
|
||||
n = len / 8;
|
||||
for (uint32 i = 0; i < n; i++)
|
||||
|
||||
for (uint32 i = 0; i < n - 1; i++)
|
||||
buffer->putLong(words[i]);
|
||||
|
||||
if (n < words.size())
|
||||
for (uint64 x = words[words.size() - 1]; x != 0; x >>= 8)
|
||||
buffer->putByte((int8) (x & 0xff));
|
||||
|
||||
for (uint64 x = words[n - 1]; x != 0; x >>= 8)
|
||||
buffer->putByte((int8) (x & 0xff));
|
||||
}
|
||||
|
||||
|
||||
void BitSet::deserialize(ByteBuffer* buffer, DeserializableControl* control) {
|
||||
|
||||
|
||||
uint32 bytes = static_cast<uint32>(SerializeHelper::readSize(buffer, control)); // in bytes
|
||||
|
||||
size_t wordsInUse = (bytes + 7) / BYTES_PER_WORD;
|
||||
words.resize(wordsInUse);
|
||||
|
||||
|
||||
wordsInUse = (bytes + 7) / 8;
|
||||
if (wordsInUse > wordsLength)
|
||||
{
|
||||
if (words) delete[] words;
|
||||
words = new uint64[wordsInUse];
|
||||
wordsLength = wordsInUse;
|
||||
}
|
||||
|
||||
if (wordsInUse == 0)
|
||||
return;
|
||||
|
||||
|
||||
control->ensureData(bytes);
|
||||
|
||||
|
||||
uint32 i = 0;
|
||||
uint32 longs = bytes / 8;
|
||||
while (i < longs)
|
||||
words[i++] = buffer->getLong();
|
||||
|
||||
|
||||
for (uint32 j = i; j < wordsInUse; j++)
|
||||
words[j] = 0;
|
||||
|
||||
|
||||
for (uint32 remaining = (bytes - longs * 8), j = 0; j < remaining; j++)
|
||||
words[i] |= (buffer->getByte() & 0xffLL) << (8 * j);
|
||||
|
||||
recalculateWordsInUse(); // Sender shouldn't add extra zero bytes, but don't fail it it does
|
||||
words[i] |= (buffer->getByte() & 0xffL) << (8 * j);
|
||||
|
||||
}
|
||||
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const BitSet& b)
|
||||
{
|
||||
o << '{';
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* bitSet.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
@@ -9,11 +10,7 @@
|
||||
#ifndef BITSET_H
|
||||
#define BITSET_H
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
# include <initializer_list>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/serialize.h>
|
||||
@@ -50,8 +47,6 @@ namespace epics { namespace pvData {
|
||||
* synchronization.
|
||||
*
|
||||
* Based on Java implementation.
|
||||
*
|
||||
* @since 7.0.0 Many methods return BitSet& to facilite method chaining.
|
||||
*/
|
||||
class epicsShareClass BitSet : public Serializable {
|
||||
public:
|
||||
@@ -71,16 +66,6 @@ namespace epics { namespace pvData {
|
||||
*/
|
||||
BitSet(uint32 nbits);
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
/** Initialize from a list of indicies
|
||||
@code
|
||||
BitSet X({1, 5});
|
||||
assert(X.get(1) && X.get(5));
|
||||
@endcode
|
||||
*/
|
||||
BitSet(std::initializer_list<uint32> I);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
@@ -92,21 +77,21 @@ namespace epics { namespace pvData {
|
||||
*
|
||||
* @param bitIndex the index of the bit to flip
|
||||
*/
|
||||
BitSet& flip(uint32 bitIndex);
|
||||
void flip(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit at the specified index to @c true.
|
||||
*
|
||||
* @param bitIndex a bit index
|
||||
*/
|
||||
BitSet& set(uint32 bitIndex);
|
||||
void set(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit specified by the index to @c false.
|
||||
*
|
||||
* @param bitIndex the index of the bit to be cleared
|
||||
*/
|
||||
BitSet& clear(uint32 bitIndex);
|
||||
void clear(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit at the specified index to the specified value.
|
||||
@@ -183,13 +168,8 @@ namespace epics { namespace pvData {
|
||||
*/
|
||||
uint32 size() const;
|
||||
|
||||
//! Returns true if any bit is set in both *this and other
|
||||
bool logical_and(const BitSet& other) const;
|
||||
//! Returns true if any bit is set in both *this or other
|
||||
bool logical_or(const BitSet& other) const;
|
||||
|
||||
/**
|
||||
* Performs a bitwise <b>AND</b> of this target bit set with the
|
||||
* Performs a logical <b>AND</b> of this target bit set with the
|
||||
* argument bit set. This bit set is modified so that each bit in it
|
||||
* has the value @c true if and only if it both initially
|
||||
* had the value @c true and the corresponding bit in the
|
||||
@@ -200,7 +180,7 @@ namespace epics { namespace pvData {
|
||||
BitSet& operator&=(const BitSet& set);
|
||||
|
||||
/**
|
||||
* Performs a bitwise <b>OR</b> of this bit set with the bit set
|
||||
* Performs a logical <b>OR</b> of this bit set with the bit set
|
||||
* argument. This bit set is modified so that a bit in it has the
|
||||
* value @c true if and only if it either already had the
|
||||
* value @c true or the corresponding bit in the bit set
|
||||
@@ -211,7 +191,7 @@ namespace epics { namespace pvData {
|
||||
BitSet& operator|=(const BitSet& set);
|
||||
|
||||
/**
|
||||
* Performs a bitwise <b>XOR</b> of this bit set with the bit set
|
||||
* Performs a logical <b>XOR</b> of this bit set with the bit set
|
||||
* argument. This bit set is modified so that a bit in it has the
|
||||
* value @c true if and only if one of the following
|
||||
* statements holds:
|
||||
@@ -231,9 +211,6 @@ namespace epics { namespace pvData {
|
||||
*/
|
||||
BitSet& operator=(const BitSet &set);
|
||||
|
||||
//! Swap contents
|
||||
void swap(BitSet& set);
|
||||
|
||||
/**
|
||||
* Perform AND operation on <code>set1</code> and <code>set2</code>,
|
||||
* and OR on result and this instance.
|
||||
@@ -256,11 +233,42 @@ namespace epics { namespace pvData {
|
||||
|
||||
private:
|
||||
|
||||
typedef std::vector<uint64> words_t;
|
||||
/*
|
||||
* BitSets are packed into arrays of "words." Currently a word is
|
||||
* a long, which consists of 64 bits, requiring 6 address bits.
|
||||
* The choice of word size is determined purely by performance concerns.
|
||||
*/
|
||||
static const uint32 ADDRESS_BITS_PER_WORD = 6;
|
||||
static const uint32 BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
|
||||
static const uint32 BIT_INDEX_MASK = BITS_PER_WORD - 1;
|
||||
|
||||
/** Used to shift left or right for a partial word mask */
|
||||
static const uint64 WORD_MASK = ~((uint64)0);
|
||||
|
||||
/** The internal field corresponding to the serialField "bits". */
|
||||
words_t words;
|
||||
uint64* words;
|
||||
|
||||
/** The internal field corresponding to the size of words[] array. */
|
||||
uint32 wordsLength;
|
||||
|
||||
/** The number of words in the logical size of this BitSet. */
|
||||
uint32 wordsInUse;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Given a bit index, return word index containing it.
|
||||
*/
|
||||
static inline uint32 wordIndex(uint32 bitIndex) {
|
||||
return bitIndex >> ADDRESS_BITS_PER_WORD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new word array.
|
||||
*/
|
||||
void initWords(uint32 nbits);
|
||||
|
||||
/**
|
||||
* Sets the field wordsInUse to the logical size in words of the bit set.
|
||||
* WARNING: This method assumes that the number of words actually in use is
|
||||
@@ -1,10 +1,8 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/byteBuffer.h>
|
||||
|
||||
953
src/misc/byteBuffer.h
Normal file
953
src/misc/byteBuffer.h
Normal file
@@ -0,0 +1,953 @@
|
||||
/* byteBuffer.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
#ifndef BYTEBUFFER_H
|
||||
#define BYTEBUFFER_H
|
||||
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
#define byteBufferepicsExportSharedSymbols
|
||||
#undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <epicsEndian.h>
|
||||
|
||||
#ifdef byteBufferepicsExportSharedSymbols
|
||||
#define epicsExportSharedSymbols
|
||||
#undef byteBufferepicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvData {
|
||||
|
||||
/*
|
||||
TODO can be used:
|
||||
|
||||
MS Visual C++:
|
||||
|
||||
You include intrin.h and call the following functions:
|
||||
|
||||
For 16 bit numbers:
|
||||
unsigned short _byteswap_ushort(unsigned short value);
|
||||
|
||||
For 32 bit numbers:
|
||||
unsigned long _byteswap_ulong(unsigned long value);
|
||||
|
||||
For 64 bit numbers:
|
||||
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
|
||||
*/
|
||||
|
||||
/*
|
||||
For floats and doubles it's more difficult as with plain integers as these may or not may be in the host machines byte-order.
|
||||
You can get little-endian floats on big-endian machines and vice versa.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define GCC_VERSION_SINCE(major, minor, patchlevel) \
|
||||
(defined(__GNUC__) && !defined(__INTEL_COMPILER) && \
|
||||
((__GNUC__ > (major)) || \
|
||||
(__GNUC__ == (major) && __GNUC_MINOR__ > (minor)) || \
|
||||
(__GNUC__ == (major) && __GNUC_MINOR__ == (minor) && __GNUC_PATCHLEVEL__ >= (patchlevel))))
|
||||
|
||||
|
||||
#if GCC_VERSION_SINCE(4,3,0)
|
||||
|
||||
#define swap32(x) __builtin_bswap32(x)
|
||||
#define swap64(x) __builtin_bswap64(x)
|
||||
|
||||
#define __byte_swap16(x) \
|
||||
(((x) >> 8) | \
|
||||
((x) << 8))
|
||||
|
||||
static inline uint16_t
|
||||
swap16(uint16_t _x)
|
||||
{
|
||||
return (__byte_swap16(_x));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
#define __byte_swap16(x) \
|
||||
(((x) >> 8) | \
|
||||
((x) << 8))
|
||||
|
||||
#define __byte_swap32(x) \
|
||||
((((x) & 0xff000000) >> 24) | \
|
||||
(((x) & 0x00ff0000) >> 8) | \
|
||||
(((x) & 0x0000ff00) << 8) | \
|
||||
(((x) & 0x000000ff) << 24))
|
||||
|
||||
#define __byte_swap64(x) \
|
||||
(((x) >> 56) | \
|
||||
(((x) >> 40) & 0xff00) | \
|
||||
(((x) >> 24) & 0xff0000) | \
|
||||
(((x) >> 8) & 0xff000000) | \
|
||||
(((x) << 8) & ((uint64_t)0xff << 32)) | \
|
||||
(((x) << 24) & ((uint64_t)0xff << 40)) | \
|
||||
(((x) << 40) & ((uint64_t)0xff << 48)) | \
|
||||
(((x) << 56)))
|
||||
|
||||
static inline uint16_t
|
||||
swap16(uint16_t _x)
|
||||
{
|
||||
return (__byte_swap16(_x));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
swap32(uint32_t _x)
|
||||
{
|
||||
return (__byte_swap32(_x));
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
swap64(uint64_t _x)
|
||||
{
|
||||
return (__byte_swap64(_x));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline T swap(T val) { return val; } // not valid
|
||||
|
||||
template<>
|
||||
inline int16 swap(int16 val)
|
||||
{
|
||||
return swap16(val);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int32 swap(int32 val)
|
||||
{
|
||||
return swap32(val);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int64 swap(int64 val)
|
||||
{
|
||||
return swap64(val);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint16 swap(uint16 val)
|
||||
{
|
||||
return swap16(val);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint32 swap(uint32 val)
|
||||
{
|
||||
return swap32(val);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint64 swap(uint64 val)
|
||||
{
|
||||
return swap64(val);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline float swap(float val)
|
||||
{
|
||||
union {
|
||||
int32 i;
|
||||
float f;
|
||||
} conv;
|
||||
conv.f = val;
|
||||
conv.i = swap32(conv.i);
|
||||
return conv.f;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double swap(double val)
|
||||
{
|
||||
union {
|
||||
int64 i;
|
||||
double d;
|
||||
} conv;
|
||||
conv.d = val;
|
||||
conv.i = swap64(conv.i);
|
||||
return conv.d;
|
||||
}
|
||||
|
||||
#define is_aligned(POINTER, BYTE_COUNT) \
|
||||
(((std::ptrdiff_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0)
|
||||
|
||||
/*template <bool ENDIANESS_SUPPORT = false,
|
||||
bool UNALIGNED_ACCESS = false,
|
||||
bool ADAPTIVE_ACCESS = true,
|
||||
bool USE_INLINE_MEMCPY = true>*/
|
||||
|
||||
#define ENDIANESS_SUPPORT true
|
||||
#define UNALIGNED_ACCESS true
|
||||
#define ADAPTIVE_ACCESS true
|
||||
#define USE_INLINE_MEMCPY true
|
||||
|
||||
#if defined (__GNUC__) && (__GNUC__ < 3)
|
||||
#define GET(T) get((T*)0)
|
||||
#else
|
||||
#define GET(T) get<T>()
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief This class implements a Bytebuffer that is like the java.nio.ByteBuffer.
|
||||
*
|
||||
* <p>A @c BitSet is not safe for multithreaded use without
|
||||
* external synchronization.
|
||||
*
|
||||
* Based on Java implementation.
|
||||
*/
|
||||
class ByteBuffer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param size The number of bytes.
|
||||
* @param byteOrder The byte order.
|
||||
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG.
|
||||
*/
|
||||
ByteBuffer(std::size_t size, int byteOrder = EPICS_BYTE_ORDER) :
|
||||
_buffer(0), _size(size),
|
||||
_reverseEndianess(byteOrder != EPICS_BYTE_ORDER),
|
||||
_reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER),
|
||||
_wrapped(false)
|
||||
{
|
||||
_buffer = (char*)malloc(size);
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for wrapping existing buffers.
|
||||
* Given buffer will not be released by the ByteBuffer instance.
|
||||
* @param buffer Existing buffer.
|
||||
* @param size The number of bytes.
|
||||
* @param byteOrder The byte order.
|
||||
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG.
|
||||
*/
|
||||
ByteBuffer(char* buffer, std::size_t size, int byteOrder = EPICS_BYTE_ORDER) :
|
||||
_buffer(buffer), _size(size),
|
||||
_reverseEndianess(byteOrder != EPICS_BYTE_ORDER),
|
||||
_reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER),
|
||||
_wrapped(true)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~ByteBuffer()
|
||||
{
|
||||
if (_buffer && !_wrapped) free(_buffer);
|
||||
}
|
||||
/**
|
||||
* Set the byte order.
|
||||
*
|
||||
* @param byteOrder The byte order.
|
||||
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG,
|
||||
*/
|
||||
inline void setEndianess(int byteOrder)
|
||||
{
|
||||
_reverseEndianess = (byteOrder != EPICS_BYTE_ORDER);
|
||||
_reverseFloatEndianess = (byteOrder != EPICS_FLOAT_WORD_ORDER);
|
||||
}
|
||||
/**
|
||||
* Get the raw buffer data.
|
||||
* @return the raw buffer data.
|
||||
*/
|
||||
inline const char* getBuffer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
/**
|
||||
* Makes a buffer ready for a new sequence of channel-read or relative put operations:
|
||||
* It sets the limit to the capacity and the position to zero.
|
||||
*/
|
||||
inline void clear()
|
||||
{
|
||||
_position = _buffer;
|
||||
_limit = _buffer + _size;
|
||||
}
|
||||
/**
|
||||
* Makes a buffer ready for a new sequence of channel-write or relative get operations:
|
||||
* It sets the limit to the current position and then sets the position to zero.
|
||||
*/
|
||||
inline void flip() {
|
||||
_limit = _position;
|
||||
_position = _buffer;
|
||||
}
|
||||
/**
|
||||
* Makes a buffer ready for re-reading the data that it already contains:
|
||||
* It leaves the limit unchanged and sets the position to zero.
|
||||
*/
|
||||
inline void rewind() {
|
||||
_position = _buffer;
|
||||
}
|
||||
/**
|
||||
* Returns the current position.
|
||||
* @return The current position in the raw data.
|
||||
*/
|
||||
inline std::size_t getPosition()
|
||||
{
|
||||
return (std::size_t)(((std::ptrdiff_t)(const void *)_position) - ((std::ptrdiff_t)(const void *)_buffer));
|
||||
}
|
||||
/**
|
||||
* Sets the buffer position.
|
||||
* If the mark is defined and larger than the new position then it is discarded.
|
||||
*
|
||||
* @param pos The offset into the raw buffer.
|
||||
* The new position value; must be no larger than the current limit
|
||||
*/
|
||||
inline void setPosition(std::size_t pos)
|
||||
{
|
||||
_position = _buffer + pos;
|
||||
}
|
||||
/**
|
||||
* Returns this buffer's limit.
|
||||
*
|
||||
* @return The offset into the raw buffer.
|
||||
*/
|
||||
inline std::size_t getLimit()
|
||||
{
|
||||
return (std::size_t)(((std::ptrdiff_t)(const void *)_limit) - ((std::ptrdiff_t)(const void *)_buffer));
|
||||
}
|
||||
/**
|
||||
* Sets this buffer's limit.
|
||||
* If the position is larger than the new limit then it is set to the new limit.s
|
||||
* If the mark is defined and larger than the new limit then it is discarded.
|
||||
*
|
||||
* @param limit The new position value;
|
||||
* must be no larger than the current limit
|
||||
*/
|
||||
inline void setLimit(std::size_t limit)
|
||||
{
|
||||
_limit = _buffer + limit;
|
||||
}
|
||||
/**
|
||||
* Returns the number of elements between the current position and the limit.
|
||||
*
|
||||
* @return The number of elements remaining in this buffer.
|
||||
*/
|
||||
inline std::size_t getRemaining()
|
||||
{
|
||||
return (std::size_t)(((std::ptrdiff_t)(const void *)_limit) - ((std::ptrdiff_t)(const void *)_position));
|
||||
}
|
||||
/**
|
||||
* Returns The size, i.e. capacity of the raw data buffer in bytes.
|
||||
*
|
||||
* @return The size of the raw data buffer.
|
||||
*/
|
||||
inline std::size_t getSize()
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
/**
|
||||
* Put the value into the raw buffer as a byte stream in the current byte order.
|
||||
*
|
||||
* @param value The value to be put into the byte buffer.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void put(T value);
|
||||
/**
|
||||
* Put the value into the raw buffer at the specified index as a byte stream in the current byte order.
|
||||
*
|
||||
* @param index Offset in the byte buffer.
|
||||
* @param value The value to be put into the byte buffer.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void put(std::size_t index, T value);
|
||||
/**
|
||||
* Get the new object from the byte buffer. The item MUST have type @c T.
|
||||
* The position is adjusted based on the type.
|
||||
*
|
||||
* @return The object.
|
||||
*/
|
||||
#if defined (__GNUC__) && (__GNUC__ < 3)
|
||||
template<typename T>
|
||||
inline T get(const T*);
|
||||
#else
|
||||
template<typename T>
|
||||
inline T get();
|
||||
#endif
|
||||
/**
|
||||
* Get the new object from the byte buffer at the specified index.
|
||||
* The item MUST have type @c T.
|
||||
* The position is adjusted based on the type.
|
||||
*
|
||||
* @param index The location in the byte buffer.
|
||||
* @return The object.
|
||||
*/
|
||||
template<typename T>
|
||||
inline T get(std::size_t index);
|
||||
/**
|
||||
* Put a sub-array of bytes into the byte buffer.
|
||||
* The position is increased by the count.
|
||||
*
|
||||
* @param src The source array.
|
||||
* @param src_offset The starting position within src.
|
||||
* @param count The number of bytes to put into the byte buffer,
|
||||
*/
|
||||
inline void put(const char* src, std::size_t src_offset, std::size_t count) {
|
||||
//if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer overflow");
|
||||
memcpy(_position, src + src_offset, count);
|
||||
_position += count;
|
||||
}
|
||||
/**
|
||||
* Get a sub-array of bytes from the byte buffer.
|
||||
* The position is increased by the count.
|
||||
*
|
||||
* @param dest The destination array.
|
||||
* @param dest_offset The starting position within src.
|
||||
* @param count The number of bytes to put into the byte buffer.
|
||||
*/
|
||||
inline void get(char* dest, std::size_t dest_offset, std::size_t count) {
|
||||
//if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer overflow");
|
||||
memcpy(dest + dest_offset, _position, count);
|
||||
_position += count;
|
||||
}
|
||||
/**
|
||||
* Put an array of type @c T into the byte buffer.
|
||||
* The position is adjusted.
|
||||
*
|
||||
* @param values The input array.
|
||||
* @param count The number of elements.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void putArray(const T* values, std::size_t count);
|
||||
/**
|
||||
* Get an array of type @c T from the byte buffer.
|
||||
* The position is adjusted.
|
||||
*
|
||||
* @param values The destination array.
|
||||
* @param count The number of elements.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void getArray(T* values, std::size_t count);
|
||||
/**
|
||||
* Is the byte order the EPICS_BYTE_ORDER
|
||||
* @return (false,true) if (is, is not) the EPICS_BYTE_ORDER
|
||||
*/
|
||||
template<typename T>
|
||||
inline bool reverse()
|
||||
{
|
||||
return _reverseEndianess;
|
||||
}
|
||||
/**
|
||||
* Adjust position so that it is aligned to the specified size.
|
||||
* Size MUST be a power of 2.
|
||||
* @param size The alignment requirement.
|
||||
*/
|
||||
inline void align(std::size_t size)
|
||||
{
|
||||
const std::size_t k = size - 1;
|
||||
_position = (char*)((((std::ptrdiff_t)(const void *)_position) + k) & ~(k));
|
||||
}
|
||||
/**
|
||||
* Put a boolean value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putBoolean( bool value) { put< int8>(value ? 1 : 0); }
|
||||
/**
|
||||
* Put a byte value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putByte ( int8 value) { put< int8>(value); }
|
||||
/**
|
||||
* Put a short value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putShort ( int16 value) { put< int16>(value); }
|
||||
/**
|
||||
* Put an int value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putInt ( int32 value) { put< int32>(value); }
|
||||
/**
|
||||
* Put a long value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putLong ( int64 value) { put< int64>(value); }
|
||||
/**
|
||||
* Put a float value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putFloat ( float value) { put< float>(value); }
|
||||
/**
|
||||
* Put a double value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putDouble (double value) { put<double>(value); }
|
||||
|
||||
/**
|
||||
* Put a boolean value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putBoolean(std::size_t index, bool value) { put< int8>(index, value); }
|
||||
/**
|
||||
* Put a byte value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putByte (std::size_t index, int8 value) { put< int8>(index, value); }
|
||||
/**
|
||||
* Put a short value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putShort (std::size_t index, int16 value) { put< int16>(index, value); }
|
||||
/**
|
||||
* Put an int value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putInt (std::size_t index, int32 value) { put< int32>(index, value); }
|
||||
/**
|
||||
* Put a long value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putLong (std::size_t index, int64 value) { put< int64>(index, value); }
|
||||
/**
|
||||
* Put a float value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putFloat (std::size_t index, float value) { put< float>(index, value); }
|
||||
/**
|
||||
* Put a double value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putDouble (std::size_t index, double value) { put<double>(index, value); }
|
||||
/**
|
||||
* Get a boolean value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline bool getBoolean() { return GET( int8) != 0; }
|
||||
/**
|
||||
* Get a byte value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline int8 getByte () { return GET( int8); }
|
||||
/**
|
||||
* Get a short value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline int16 getShort () { return GET( int16); }
|
||||
/**
|
||||
* Get a int value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline int32 getInt () { return GET( int32); }
|
||||
/**
|
||||
* Get a long value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline int64 getLong () { return GET( int64); }
|
||||
/**
|
||||
* Get a float value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline float getFloat () { return GET( float); }
|
||||
/**
|
||||
* Get a double value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline double getDouble () { return GET(double); }
|
||||
/**
|
||||
* Get a boolean value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline bool getBoolean(std::size_t index) { return get< int8>(index) != 0; }
|
||||
/**
|
||||
* Get a byte value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline int8 getByte (std::size_t index) { return get< int8>(index); }
|
||||
/**
|
||||
* Get a short value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline int16 getShort (std::size_t index) { return get< int16>(index); }
|
||||
/**
|
||||
* Get an int value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline int32 getInt (std::size_t index) { return get< int32>(index); }
|
||||
/**
|
||||
* Get a long value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline int64 getLong (std::size_t index) { return get< int64>(index); }
|
||||
/**
|
||||
* Get a float value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline float getFloat (std::size_t index) { return get< float>(index); }
|
||||
/**
|
||||
* Get a boolean value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline double getDouble (std::size_t index) { return get<double>(index); }
|
||||
|
||||
// TODO remove
|
||||
inline const char* getArray()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
char* _buffer;
|
||||
char* _position;
|
||||
char* _limit;
|
||||
std::size_t _size;
|
||||
bool _reverseEndianess;
|
||||
bool _reverseFloatEndianess;
|
||||
bool _wrapped;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline bool ByteBuffer::reverse<bool>()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ByteBuffer::reverse<int8>()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ByteBuffer::reverse<uint8>()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ByteBuffer::reverse<float>()
|
||||
{
|
||||
return _reverseFloatEndianess;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ByteBuffer::reverse<double>()
|
||||
{
|
||||
return _reverseFloatEndianess;
|
||||
}
|
||||
|
||||
// the following methods must come after the specialized reverse<>() methods to make pre-gcc3 happy
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::put(T value)
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
*(_position++) = (int8)value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
value = swap<T>(value);
|
||||
}
|
||||
|
||||
if (UNALIGNED_ACCESS)
|
||||
{
|
||||
// NOTE: some CPU handle unaligned access pretty good (e.g. x86)
|
||||
*((T*)_position) = value;
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: this check and branching does not always pay off
|
||||
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
|
||||
{
|
||||
*((T*)_position) = value;
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (USE_INLINE_MEMCPY)
|
||||
{
|
||||
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
|
||||
memcpy(_position, &value, sizeof(T));
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: compiler should optimize this and unroll the loop
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
_position[i] = ((char*)&value)[i];
|
||||
_position += sizeof(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::put(std::size_t index, T value)
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
*(_buffer + index) = (int8)value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
value = swap<T>(value);
|
||||
}
|
||||
|
||||
if (UNALIGNED_ACCESS)
|
||||
{
|
||||
// NOTE: some CPU handle unaligned access pretty good (e.g. x86)
|
||||
*((T*)(_buffer + index)) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: this check and branching does not always pay off
|
||||
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
|
||||
{
|
||||
*((T*)(_buffer + index)) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (USE_INLINE_MEMCPY)
|
||||
{
|
||||
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
|
||||
memcpy(_buffer + index, &value, sizeof(T));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: compiler should optimize this and unroll the loop
|
||||
char *p = _buffer + index;
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
p[i] = ((char*)&value)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if defined (__GNUC__) && (__GNUC__ < 3)
|
||||
template<typename T>
|
||||
inline T ByteBuffer::get(const T*)
|
||||
#else
|
||||
template<typename T>
|
||||
inline T ByteBuffer::get()
|
||||
#endif
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
return (int8)(*(_position++));
|
||||
}
|
||||
|
||||
|
||||
T value;
|
||||
|
||||
if (UNALIGNED_ACCESS)
|
||||
{
|
||||
// NOTE: some CPU handle unaligned access pretty good (e.g. x86)
|
||||
value = *((T*)_position);
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: this check and branching does not always pay off
|
||||
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
|
||||
{
|
||||
value = *((T*)_position);
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (USE_INLINE_MEMCPY)
|
||||
{
|
||||
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
|
||||
memcpy(&value, _position, sizeof(T));
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: compiler should optimize this and unroll the loop
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
((char*)&value)[i] = _position[i];
|
||||
_position += sizeof(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
value = swap<T>(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T ByteBuffer::get(std::size_t index)
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
return (int8)(*(_buffer + index));
|
||||
}
|
||||
|
||||
|
||||
T value;
|
||||
|
||||
if (UNALIGNED_ACCESS)
|
||||
{
|
||||
// NOTE: some CPU handle unaligned access pretty good (e.g. x86)
|
||||
value = *((T*)(_buffer + index));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: this check and branching does not always pay off
|
||||
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
|
||||
{
|
||||
value = *((T*)(_buffer + index));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (USE_INLINE_MEMCPY)
|
||||
{
|
||||
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
|
||||
memcpy(&value, _buffer + index, sizeof(T));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: compiler should optimize this and unroll the loop
|
||||
char* p = _buffer + index;
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
((char*)&value)[i] = p[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
value = swap<T>(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::putArray(const T* values, std::size_t count)
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
put((const char*)values, 0, count);
|
||||
return;
|
||||
}
|
||||
|
||||
T* start = (T*)_position;
|
||||
|
||||
size_t n = sizeof(T)*count;
|
||||
// we require aligned arrays...
|
||||
memcpy(_position, values, n);
|
||||
_position += n;
|
||||
|
||||
// ... so that we can be fast changing endianness
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
for (std::size_t i = 0; i < count; i++)
|
||||
{
|
||||
*start = swap<T>(*start);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::getArray(T* values, std::size_t count)
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
get((char*)values, 0, count);
|
||||
return;
|
||||
}
|
||||
|
||||
T* start = (T*)values;
|
||||
|
||||
size_t n = sizeof(T)*count;
|
||||
// we require aligned arrays...
|
||||
memcpy(values, _position, n);
|
||||
_position += n;
|
||||
|
||||
// ... so that we can be fast changing endianness
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
for (std::size_t i = 0; i < count; i++)
|
||||
{
|
||||
*start = swap<T>(*start);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif /* BYTEBUFFER_H */
|
||||
0
src/misc/pv/current_function.h → src/misc/current_function.h
Normal file → Executable file
0
src/misc/pv/current_function.h → src/misc/current_function.h
Normal file → Executable file
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/debugPtr.h>
|
||||
|
||||
namespace {
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
}
|
||||
|
||||
namespace epics {
|
||||
namespace debug {
|
||||
|
||||
// joins together a group of ptr_base instances
|
||||
// which all have the same dtor
|
||||
struct tracker {
|
||||
epicsMutex mutex;
|
||||
ptr_base::ref_set_t refs;
|
||||
};
|
||||
|
||||
void shared_ptr_base::track_new()
|
||||
{
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
|
||||
// create new tracker if ptr!=nullptr, otherwise clear
|
||||
void shared_ptr_base::track_new(void* ptr)
|
||||
{
|
||||
track_clear();
|
||||
if(ptr){
|
||||
track.reset(new tracker);
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
|
||||
void shared_ptr_base::track_assign(const shared_ptr_base &o)
|
||||
{
|
||||
if(track!=o.track) {
|
||||
track_clear();
|
||||
track = o.track;
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_ptr_base::track_clear()
|
||||
{
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.erase(this);
|
||||
}
|
||||
track.reset();
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
m_depth = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_ptr_base::swap(shared_ptr_base &o)
|
||||
{
|
||||
// we cheat a bit here to avoid lock order, and to lock only twice
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(&o);
|
||||
track->refs.erase(this);
|
||||
}
|
||||
track.swap(o.track);
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
track->refs.erase(&o);
|
||||
}
|
||||
//TODO: keep original somehow???
|
||||
snap_stack();
|
||||
o.snap_stack();
|
||||
}
|
||||
|
||||
void shared_ptr_base::snap_stack()
|
||||
{
|
||||
if(!track) {
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
m_depth = 0;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#if defined(EXCEPT_USE_BACKTRACE)
|
||||
{
|
||||
m_depth=backtrace(m_stack,EXCEPT_DEPTH);
|
||||
}
|
||||
#else
|
||||
{}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void shared_ptr_base::show_stack(std::ostream& strm) const
|
||||
{
|
||||
strm<<"ptr "<<this;
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
if(m_depth<=0) return;
|
||||
#endif
|
||||
#if 0 && defined(EXCEPT_USE_BACKTRACE)
|
||||
{
|
||||
|
||||
char **symbols=backtrace_symbols(m_stack, m_depth);
|
||||
|
||||
strm<<": ";
|
||||
for(int i=0; i<m_depth; i++) {
|
||||
strm<<symbols[i]<<", ";
|
||||
}
|
||||
|
||||
std::free(symbols);
|
||||
}
|
||||
#elif !defined(EXCEPT_USE_NONE)
|
||||
{
|
||||
strm<<": ";
|
||||
for(int i=0; i<m_depth; i++) {
|
||||
strm<<std::hex<<m_stack[i]<<" ";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void ptr_base::show_refs(std::ostream& strm, bool self, bool weak) const
|
||||
{
|
||||
if(!track) {
|
||||
strm<<"# No refs\n";
|
||||
} else {
|
||||
Guard G(track->mutex);
|
||||
for(auto ref : track->refs) {
|
||||
if(!self && ref==this) continue;
|
||||
strm<<'#';
|
||||
ref->show_stack(strm);
|
||||
strm<<'\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ptr_base::spy_refs(ref_set_t &refs) const
|
||||
{
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
refs.insert(track->refs.begin(), track->refs.end());
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace epics::debug
|
||||
|
||||
#endif // __cplusplus>=201103L
|
||||
41
src/misc/destroyable.h
Normal file
41
src/misc/destroyable.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* destroyable.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
#ifndef DESTROYABLE_H
|
||||
#define DESTROYABLE_H
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Instance declaring destroy method.
|
||||
*
|
||||
* @author mse
|
||||
*/
|
||||
class epicsShareClass Destroyable {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Destroyable);
|
||||
/**
|
||||
* Destroy this instance.
|
||||
*/
|
||||
virtual void destroy() = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Do not allow delete on this instance and derived classes, destroy() must be used instead.
|
||||
*/
|
||||
virtual ~Destroyable() {};
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* DESTROYABLE_H */
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mes
|
||||
@@ -9,7 +10,6 @@
|
||||
#include <sstream>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
@@ -50,7 +50,7 @@ ExceptionMixin::show() const
|
||||
out<<symbols[i]<<"\n";
|
||||
}
|
||||
|
||||
std::free(symbols);
|
||||
free(symbols);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/*
|
||||
* epicsException.h
|
||||
* epicsException.hpp
|
||||
*
|
||||
* Created on: Oct 20, 2010
|
||||
* Author: Matej Sekoranja
|
||||
@@ -33,10 +34,19 @@
|
||||
#ifndef EPICSEXCEPTION_H_
|
||||
#define EPICSEXCEPTION_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning( push )
|
||||
#pragma warning(disable: 4275) // warning C4275: non dll-interface class used as base for dll-interface class (std::logic_error)
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
// Users may redefine this for a large size if desired
|
||||
@@ -48,7 +58,7 @@
|
||||
# include <execinfo.h>
|
||||
# include <cxxabi.h>
|
||||
# define EXCEPT_USE_BACKTRACE
|
||||
#elif defined(_WIN32) && !defined(_MINGW) && !defined(SKIP_DBGHELP)
|
||||
#elif defined(_WIN32) && !defined(__MINGW__) && !defined(SKIP_DBGHELP)
|
||||
# define _WINSOCKAPI_
|
||||
# include <windows.h>
|
||||
# include <dbghelp.h>
|
||||
@@ -57,12 +67,6 @@
|
||||
# define EXCEPT_USE_NONE
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(_MINGW)
|
||||
#pragma warning( push )
|
||||
#pragma warning(disable: 4275) // non dll-interface class used as base for dll-interface class (std::logic_error)
|
||||
#pragma warning(disable: 4251) // class std::string needs to have dll-interface to be used by clients
|
||||
#endif
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
@@ -171,17 +175,17 @@ do { \
|
||||
|
||||
#define PRINT_EXCEPTION2(EI, FP) \
|
||||
do { \
|
||||
::epics::pvData::ExceptionMixin *_em_p=dynamic_cast< ::epics::pvData::ExceptionMixin*>(&EI); \
|
||||
ExceptionMixin *_em_p=dynamic_cast<ExceptionMixin*>(&EI); \
|
||||
if (_em_p) {_em_p->print(FP);} \
|
||||
}while(0)
|
||||
|
||||
#define PRINT_EXCEPTION(EI) PRINT_EXCEPTION2(EI,stderr)
|
||||
|
||||
#if !defined(__GNUC__) || __GNUC__ < 4
|
||||
#ifndef __GNUC__
|
||||
# define SHOW_EXCEPTION(EI) ::epics::pvData::detail::showException(EI)
|
||||
#else
|
||||
# define SHOW_EXCEPTION(EI) \
|
||||
({ ::epics::pvData::ExceptionMixin *_mx=dynamic_cast< ::epics::pvData::ExceptionMixin*>(&(EI)); \
|
||||
({ ExceptionMixin *_mx=dynamic_cast<ExceptionMixin*>(&(EI)); \
|
||||
_mx ? _mx->show() : std::string(); \
|
||||
})
|
||||
#endif
|
||||
@@ -224,7 +228,7 @@ private:
|
||||
mutable std::string base_msg;
|
||||
};
|
||||
|
||||
#if defined(_WIN32) && !defined(_MINGW)
|
||||
#ifdef _WIN32
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* event.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* event.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -12,12 +13,22 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
#define eventepicsExportSharedSymbols
|
||||
#undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <epicsEvent.h>
|
||||
#include <shareLib.h>
|
||||
|
||||
#ifdef eventepicsExportSharedSymbols
|
||||
#define epicsExportSharedSymbols
|
||||
#undef eventepicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
95
src/misc/executor.cpp
Normal file
95
src/misc/executor.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/* executor.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/executor.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
// special instance to stop the executor thread
|
||||
class ExecutorShutdown : public Command {
|
||||
virtual void command();
|
||||
};
|
||||
|
||||
void ExecutorShutdown::command()
|
||||
{
|
||||
}
|
||||
|
||||
static
|
||||
std::tr1::shared_ptr<Command> shutdown(new ExecutorShutdown());
|
||||
|
||||
|
||||
Executor::Executor(string const & threadName,ThreadPriority priority)
|
||||
: thread(threadName,priority,this)
|
||||
{
|
||||
}
|
||||
|
||||
Executor::~Executor()
|
||||
{
|
||||
execute(shutdown);
|
||||
stopped.wait();
|
||||
// The thread signals 'stopped' while still holding
|
||||
// the lock. By taking it we wait for the run() function
|
||||
// to actually return
|
||||
Lock xx(mutex);
|
||||
head.reset();
|
||||
tail.reset();
|
||||
}
|
||||
|
||||
void Executor::run()
|
||||
{
|
||||
Lock xx(mutex);
|
||||
while(true) {
|
||||
while(!head.get()) {
|
||||
xx.unlock();
|
||||
moreWork.wait();
|
||||
xx.lock();
|
||||
}
|
||||
CommandPtr command = head;
|
||||
head = command->next;
|
||||
if(!command.get()) continue;
|
||||
if(command.get()==shutdown.get()) break;
|
||||
xx.unlock();
|
||||
try {
|
||||
command->command();
|
||||
}catch(std::exception& e){
|
||||
//TODO: feed into logging mechanism
|
||||
fprintf(stderr, "Executor: Unhandled exception: %s",e.what());
|
||||
}catch(...){
|
||||
fprintf(stderr, "Executor: Unhandled exception");
|
||||
}
|
||||
|
||||
xx.lock();
|
||||
}
|
||||
stopped.signal();
|
||||
}
|
||||
|
||||
void Executor::execute(CommandPtr const & command)
|
||||
{
|
||||
Lock xx(mutex);
|
||||
command->next.reset();
|
||||
if(!head.get()) {
|
||||
head = command;
|
||||
moreWork.signal();
|
||||
return;
|
||||
}
|
||||
CommandPtr tail = head;
|
||||
while(tail->next) tail = tail->next;
|
||||
tail->next = command;
|
||||
}
|
||||
|
||||
}}
|
||||
91
src/misc/executor.h
Normal file
91
src/misc/executor.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/* executor.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef EXECUTOR_H
|
||||
#define EXECUTOR_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/thread.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class Command;
|
||||
class Executor;
|
||||
typedef std::tr1::shared_ptr<Command> CommandPtr;
|
||||
typedef std::tr1::shared_ptr<Executor> ExecutorPtr;
|
||||
|
||||
/**
|
||||
* @brief A command to be called by Executor
|
||||
*
|
||||
*/
|
||||
class epicsShareClass Command {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Command);
|
||||
/**
|
||||
*
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Command(){}
|
||||
/**
|
||||
*
|
||||
* The command that is executed.
|
||||
*/
|
||||
virtual void command() = 0;
|
||||
private:
|
||||
CommandPtr next;
|
||||
friend class Executor;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A class that executes commands.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass Executor : public Runnable{
|
||||
public:
|
||||
POINTER_DEFINITIONS(Executor);
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param threadName name for the executor thread.
|
||||
* @param priority The thread priority.
|
||||
*/
|
||||
Executor(std::string const & threadName,ThreadPriority priority);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Executor();
|
||||
/**
|
||||
*
|
||||
* Request to execute a command.
|
||||
* @param command A shared pointer to the command instance.
|
||||
*/
|
||||
void execute(CommandPtr const &command);
|
||||
/**
|
||||
*
|
||||
* The thread run method.
|
||||
*/
|
||||
virtual void run();
|
||||
private:
|
||||
CommandPtr head;
|
||||
CommandPtr tail;
|
||||
epics::pvData::Mutex mutex;
|
||||
epics::pvData::Event moreWork;
|
||||
epics::pvData::Event stopped;
|
||||
epics::pvData::Thread thread;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* EXECUTOR_H */
|
||||
@@ -1,7 +1,8 @@
|
||||
/* localStaticLock.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* localStaticLock.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
@@ -9,13 +10,11 @@
|
||||
#ifndef LOCALSTATICLOCK_H
|
||||
#define LOCALSTATICLOCK_H
|
||||
|
||||
#include <compilerDependencies.h>
|
||||
|
||||
#include <pv/lock.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
epicsShareExtern epics::pvData::Mutex& getLocalStaticInitMutex() EPICS_DEPRECATED;
|
||||
epicsShareExtern epics::pvData::Mutex& getLocalStaticInitMutex();
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
// noop
|
||||
@@ -1,7 +1,8 @@
|
||||
/* lock.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -11,11 +12,21 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
#define lockepicsExportSharedSymbols
|
||||
#undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <shareLib.h>
|
||||
|
||||
#ifdef lockepicsExportSharedSymbols
|
||||
#define epicsExportSharedSymbols
|
||||
#undef lockepicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/noDefaultMethods.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
/* This is based on item 14 of
|
||||
* Effective C++, Third Edition, Scott Meyers
|
||||
@@ -33,8 +44,7 @@ typedef epicsMutex Mutex;
|
||||
* This is based on item 14 of
|
||||
* * Effective C++, Third Edition, Scott Meyers
|
||||
*/
|
||||
class Lock {
|
||||
EPICS_NOT_COPYABLE(Lock)
|
||||
class epicsShareClass Lock : private NoDefaultMethods {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
116
src/misc/messageQueue.cpp
Normal file
116
src/misc/messageQueue.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/* messageQueue.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/messageQueue.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
MessageNode::MessageNode()
|
||||
: messageType(infoMessage)
|
||||
{}
|
||||
|
||||
string MessageNode::getMessage() const
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
MessageType MessageNode::getMessageType() const
|
||||
{
|
||||
return messageType;
|
||||
}
|
||||
|
||||
MessageQueuePtr MessageQueue::create(int size)
|
||||
{
|
||||
MessageNodePtrArray nodeArray;
|
||||
nodeArray.reserve(size);
|
||||
for(int i=0; i<size; i++) {
|
||||
nodeArray.push_back(
|
||||
MessageNodePtr(new MessageNode()));
|
||||
}
|
||||
return std::tr1::shared_ptr<MessageQueue>(new MessageQueue(nodeArray));
|
||||
}
|
||||
|
||||
MessageQueue::MessageQueue(MessageNodePtrArray &data)
|
||||
: Queue<MessageNode>(data),
|
||||
overrun(0)
|
||||
{ }
|
||||
|
||||
MessageQueue::~MessageQueue()
|
||||
{
|
||||
}
|
||||
|
||||
MessageNodePtr &MessageQueue::get() {
|
||||
if(getNumberUsed()==0) return nullNode;
|
||||
lastGet = getUsed();
|
||||
return lastGet;
|
||||
}
|
||||
|
||||
void MessageQueue::release() {
|
||||
if(lastGet.get()==NULL) return;
|
||||
releaseUsed(lastGet);
|
||||
lastGet.reset();
|
||||
}
|
||||
bool MessageQueue::put(string message,MessageType messageType,bool replaceLast)
|
||||
{
|
||||
MessageNodePtr node = getFree();
|
||||
if(node.get()!= NULL) {
|
||||
node->message = message;
|
||||
node->messageType = messageType;
|
||||
lastPut = node;
|
||||
setUsed(node);
|
||||
return true;
|
||||
}
|
||||
overrun++;
|
||||
if(replaceLast) {
|
||||
node = lastPut;
|
||||
node->message = message;
|
||||
node->messageType = messageType;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageQueue::isEmpty()
|
||||
{
|
||||
int free = getNumberFree();
|
||||
if(free==capacity()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageQueue::isFull()
|
||||
{
|
||||
if(getNumberFree()==0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int MessageQueue::getClearOverrun()
|
||||
{
|
||||
int num = overrun;
|
||||
overrun = 0;
|
||||
return num;
|
||||
}
|
||||
|
||||
MessageQueuePtr createMessageQueue(int size)
|
||||
{
|
||||
MessageNodePtrArray nodeArray;
|
||||
nodeArray.reserve(size);
|
||||
for(int i=0; i<size; i++) {
|
||||
nodeArray.push_back(
|
||||
MessageNodePtr(new MessageNode()));
|
||||
}
|
||||
return std::tr1::shared_ptr<MessageQueue>(new MessageQueue(nodeArray));
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
129
src/misc/messageQueue.h
Normal file
129
src/misc/messageQueue.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/* messageQueue.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef MESSAGEQUEUE_H
|
||||
#define MESSAGEQUEUE_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/requester.h>
|
||||
#include <pv/queue.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class MessageNode;
|
||||
class MessageQueue;
|
||||
typedef std::tr1::shared_ptr<MessageNode> MessageNodePtr;
|
||||
typedef std::vector<MessageNodePtr> MessageNodePtrArray;
|
||||
typedef std::tr1::shared_ptr<MessageQueue> MessageQueuePtr;
|
||||
|
||||
/**
|
||||
* @brief A node that can be put on a MessageQueue.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass MessageNode {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
MessageNode();
|
||||
/**
|
||||
*
|
||||
* Get the message value.
|
||||
* @return The message value.
|
||||
*/
|
||||
std::string getMessage() const;
|
||||
/**
|
||||
* Get the message type.
|
||||
* @return The message type which is defined in Requester.
|
||||
*/
|
||||
MessageType getMessageType() const;
|
||||
private:
|
||||
std::string message;
|
||||
MessageType messageType;
|
||||
friend class MessageQueue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A bounded queue for messages.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class epicsShareClass MessageQueue : public Queue<MessageNode> {
|
||||
public:
|
||||
POINTER_DEFINITIONS(MessageQueue);
|
||||
/**
|
||||
* Factory method to create a MessageQueue.
|
||||
* @param size The number of MessageNodes in the queue.
|
||||
* @return shared_ptr to MessageQueue.
|
||||
*/
|
||||
static MessageQueuePtr create(int size);
|
||||
/**
|
||||
* Constructor
|
||||
* @param nodeArray an array of shared_ptr to MessageNodes,
|
||||
*/
|
||||
MessageQueue(MessageNodePtrArray &nodeArray);
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~MessageQueue();
|
||||
/**
|
||||
* get the next MessageNode of the queue.
|
||||
* @return A shared_ptr to the MessageNode.
|
||||
* This will be a null pointer if queue is empty.
|
||||
* If get is successful then release for this MessageNode
|
||||
* must be called before another get can be issued.
|
||||
*/
|
||||
MessageNodePtr &get();
|
||||
/**
|
||||
* Release the MessageNode that was returned by the previous call to get.
|
||||
*/
|
||||
void release();
|
||||
/**
|
||||
*
|
||||
* put a message into the message queue
|
||||
* @param message The message string.
|
||||
* @param messageType The message type as defined in Requester,
|
||||
* @param replaceLast If true and queue is full then replace.
|
||||
* @return (false,true) if a message (was not, was) put in queiue.
|
||||
*/
|
||||
bool put(std::string message,MessageType messageType,bool replaceLast);
|
||||
/**
|
||||
* Is queue empty?
|
||||
* @return (false,true) if (is not, is) empty.
|
||||
*/
|
||||
bool isEmpty() ;
|
||||
/**
|
||||
* Is queue full?
|
||||
* @return (false,true) if (is not, is) full.
|
||||
*/
|
||||
bool isFull() ;
|
||||
/**
|
||||
*
|
||||
* Clear number of times queue was overrun and return the number
|
||||
* of times the queue was overrun.
|
||||
*/
|
||||
int getClearOverrun();
|
||||
private:
|
||||
MessageNodePtr nullNode;
|
||||
MessageNodePtr lastGet;
|
||||
MessageNodePtr lastPut;
|
||||
uint32 overrun;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* MESSAGEQUEUE_H */
|
||||
|
||||
|
||||
|
||||
42
src/misc/noDefaultMethods.h
Normal file
42
src/misc/noDefaultMethods.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* noDefaultMethods.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef NO_DEFAULT_METHODS_H
|
||||
#define NO_DEFAULT_METHODS_H
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
/* This is based on Item 6 of
|
||||
* Effective C++, Third Edition, Scott Meyers
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Base class for not allowing default methods.
|
||||
*
|
||||
* Note that copy constructor a copy methods are declared private.
|
||||
*/
|
||||
class epicsShareClass NoDefaultMethods {
|
||||
protected:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
NoDefaultMethods(){};
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~NoDefaultMethods(){}
|
||||
private:
|
||||
// do not implement
|
||||
NoDefaultMethods(const NoDefaultMethods&);
|
||||
NoDefaultMethods & operator=(const NoDefaultMethods &);
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* NO_DEFAULT_METHODS_H */
|
||||
@@ -1,7 +1,3 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
@@ -16,7 +12,7 @@
|
||||
#include <epicsConvert.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/typeCast.h"
|
||||
#include "typeCast.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
@@ -31,7 +27,7 @@ using std::string;
|
||||
#endif
|
||||
|
||||
#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1)
|
||||
/* These integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */
|
||||
/* integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */
|
||||
|
||||
#define S_stdlib_noConversion 1 /* No digits to convert */
|
||||
#define S_stdlib_extraneous 2 /* Extraneous characters */
|
||||
@@ -253,19 +249,18 @@ epicsParseFloat(const char *str, float *to, char **units)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sometimes we have to provide our own copy of strtoll()
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1800
|
||||
// On Windows with MSVC, Base-3.15 provides strtoll()
|
||||
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
|
||||
#elif defined(vxWorks)
|
||||
// On VxWorks, Base-3.15 provides strtoll()
|
||||
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
|
||||
// MS Visual Studio 2013 defines strtoll, etc.
|
||||
#if defined(_WIN32)
|
||||
# if (_MSC_VER >= 1800)
|
||||
# define WIN_NEEDS_OLL_FUNC 0
|
||||
# else
|
||||
# define WIN_NEEDS_OLL_FUNC 1
|
||||
# endif
|
||||
#else
|
||||
// Other architectures all provide strtoll()
|
||||
# define NEED_OLL_FUNCS 0
|
||||
# define WIN_NEEDS_OLL_FUNC 0
|
||||
#endif
|
||||
|
||||
#if defined(NEED_LONGLONG) && NEED_OLL_FUNCS
|
||||
#if defined(NEED_LONGLONG) && (defined(__vxworks) || WIN_NEEDS_OLL_FUNC)
|
||||
static
|
||||
long long strtoll(const char *ptr, char ** endp, int base)
|
||||
{
|
||||
@@ -313,10 +308,8 @@ noconvert:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(vxWorks)
|
||||
/* The VxWorks version of std::istringstream >> uint64_t is buggy,
|
||||
* provide our own implementation
|
||||
*/
|
||||
#if defined(__vxworks)
|
||||
/* vxworks version of std::istringstream >>uint64_t is buggy, we use out own implementation */
|
||||
static
|
||||
unsigned long long strtoull(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
@@ -507,20 +500,20 @@ void handleParseError(int err)
|
||||
|
||||
namespace epics { namespace pvData { namespace detail {
|
||||
|
||||
void parseToPOD(const char* in, boolean *out)
|
||||
void parseToPOD(const string & in, boolean *out)
|
||||
{
|
||||
if(epicsStrCaseCmp(in,"true")==0)
|
||||
if(epicsStrCaseCmp(in.c_str(),"true")==0)
|
||||
*out = 1;
|
||||
else if(epicsStrCaseCmp(in,"false")==0)
|
||||
else if(epicsStrCaseCmp(in.c_str(),"false")==0)
|
||||
*out = 0;
|
||||
else
|
||||
throw std::runtime_error("parseToPOD: string no match true/false");
|
||||
}
|
||||
|
||||
#define INTFN(T, S) \
|
||||
void parseToPOD(const char* in, T *out) { \
|
||||
void parseToPOD(const string& in, T *out) { \
|
||||
epics ## S temp; \
|
||||
int err = epicsParse ## S (in, &temp, 0, NULL); \
|
||||
int err = epicsParse ## S (in.c_str(), &temp, 0, NULL); \
|
||||
if(err) handleParseError(err); \
|
||||
else *out = temp; \
|
||||
}
|
||||
@@ -532,39 +525,38 @@ INTFN(uint16_t, UInt16);
|
||||
INTFN(int32_t, Int32);
|
||||
INTFN(uint32_t, UInt32);
|
||||
|
||||
void parseToPOD(const char* in, int64_t *out) {
|
||||
void parseToPOD(const string& in, int64_t *out) {
|
||||
#ifdef NEED_LONGLONG
|
||||
int err = epicsParseLongLong(in, out, 0, NULL);
|
||||
int err = epicsParseLongLong(in.c_str(), out, 0, NULL);
|
||||
#else
|
||||
int err = epicsParseLong(in, out, 0, NULL);
|
||||
int err = epicsParseLong(in.c_str(), out, 0, NULL);
|
||||
#endif
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const char* in, uint64_t *out) {
|
||||
void parseToPOD(const string& in, uint64_t *out) {
|
||||
#ifdef NEED_LONGLONG
|
||||
int err = epicsParseULongLong(in, out, 0, NULL);
|
||||
int err = epicsParseULongLong(in.c_str(), out, 0, NULL);
|
||||
#else
|
||||
int err = epicsParseULong(in, out, 0, NULL);
|
||||
int err = epicsParseULong(in.c_str(), out, 0, NULL);
|
||||
#endif
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const char* in, float *out) {
|
||||
int err = epicsParseFloat(in, out, NULL);
|
||||
void parseToPOD(const string& in, float *out) {
|
||||
int err = epicsParseFloat(in.c_str(), out, NULL);
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const char* in, double *out) {
|
||||
int err = epicsParseDouble(in, out, NULL);
|
||||
void parseToPOD(const string& in, double *out) {
|
||||
int err = epicsParseDouble(in.c_str(), out, NULL);
|
||||
if(err) handleParseError(err);
|
||||
#if defined(vxWorks)
|
||||
/* vxWorks strtod returns [-]epicsINF when it should return ERANGE error.
|
||||
* If [-]epicsINF is returned and the first char is a digit we translate
|
||||
* this into an ERANGE error
|
||||
#if defined(__vxworks)
|
||||
/* vxWorks strtod returns [-]epicsINF when it should return ERANGE error
|
||||
* if [-]epicsINF is returned and first char is a digit then translate this into ERANGE error
|
||||
*/
|
||||
else if (*out == epicsINF || *out == -epicsINF) {
|
||||
const char* s = in;
|
||||
const char* s = in.c_str();
|
||||
int c;
|
||||
|
||||
/* skip spaces and the sign */
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
#ifndef PV_ANYSCALAR_H
|
||||
#define PV_ANYSCALAR_H
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
# include <type_traits>
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#include <pv/templateMeta.h>
|
||||
#include <pv/typeCast.h>
|
||||
#include <pv/pvIntrospect.h> /* for ScalarType enum */
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
namespace detail {
|
||||
|
||||
// special mangling for AnyScalar ctor to map from argument type to storage type.
|
||||
// allow construction from constants.
|
||||
template <typename T>
|
||||
struct any_storage_type { typedef T type; };
|
||||
template<> struct any_storage_type<int> { typedef int32 type; };
|
||||
template<> struct any_storage_type<unsigned> { typedef uint32 type; };
|
||||
template<> struct any_storage_type<char*> { typedef std::string type; };
|
||||
template<> struct any_storage_type<const char*> { typedef std::string type; };
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
// std::max() isn't constexpr until c++14 :(
|
||||
constexpr size_t cmax(size_t A, size_t B) {
|
||||
return A>B ? A : B;
|
||||
}
|
||||
#endif
|
||||
|
||||
}// namespace detail
|
||||
|
||||
/** A type-safe variant union capable of holding
|
||||
* any of the PVD scalar types (POD or string)
|
||||
*
|
||||
@code
|
||||
AnyScalar A(5);
|
||||
assert(A.type()==pvInt);
|
||||
assert(A.ref<int32>()==5);
|
||||
assert(A.as<int32>()==5);
|
||||
assert(A.as<double>()==5.0);
|
||||
assert(A.ref<double>()==5.0); // throws AnyScalar::bad_cast
|
||||
@endcode
|
||||
*/
|
||||
class epicsShareClass AnyScalar {
|
||||
public:
|
||||
struct bad_cast : public std::exception {
|
||||
#if __cplusplus>=201103L
|
||||
bad_cast() noexcept {}
|
||||
virtual ~bad_cast() noexcept {}
|
||||
virtual const char* what() const noexcept
|
||||
#else
|
||||
bad_cast() throw() {}
|
||||
virtual ~bad_cast() throw() {}
|
||||
virtual const char* what() const throw()
|
||||
#endif
|
||||
{ return "bad_cast() type mis-match"; }
|
||||
};
|
||||
|
||||
private:
|
||||
ScalarType _stype;
|
||||
|
||||
// always reserve enough storage for std::string or double (assumed worst case)
|
||||
#if __cplusplus>=201103L
|
||||
|
||||
struct wrap_t {
|
||||
typename std::aligned_storage<detail::cmax(sizeof(std::string), sizeof(double)),
|
||||
detail::cmax(alignof(std::string), alignof(double))
|
||||
>::type blob[1];
|
||||
} _wrap;
|
||||
#else
|
||||
struct wrap_t {
|
||||
union blob_t {
|
||||
char data[sizeof(std::string)];
|
||||
double align_f; // assume std::string alignment <= 8
|
||||
} blob[1];
|
||||
} _wrap;
|
||||
#endif
|
||||
|
||||
// assumed largest non-string type
|
||||
typedef double _largest_blob;
|
||||
|
||||
template<typename T>
|
||||
inline T& _as() {
|
||||
return *reinterpret_cast<T*>(_wrap.blob);
|
||||
}
|
||||
template<typename T>
|
||||
inline const T& _as() const {
|
||||
return *reinterpret_cast<const T*>(_wrap.blob);
|
||||
}
|
||||
public:
|
||||
//! Construct empty
|
||||
//! @post empty()==true
|
||||
AnyScalar() : _stype((ScalarType)-1) {}
|
||||
|
||||
//! Construct from provided value.
|
||||
template<typename T>
|
||||
explicit AnyScalar(T v)
|
||||
{
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
STATIC_ASSERT(sizeof(TT)<=sizeof(_wrap.blob));
|
||||
|
||||
new (_wrap.blob) TT(v);
|
||||
|
||||
// this line fails to compile when type T can't be mapped to one of
|
||||
// the PVD scalar types.
|
||||
_stype = (ScalarType)ScalarTypeID<TT>::value;
|
||||
}
|
||||
|
||||
//! Construct from un-typed pointer.
|
||||
//! Caller is responsible to ensure that buf actually points to the provided type
|
||||
//! @version Added after 7.0.0
|
||||
AnyScalar(ScalarType type, const void *buf);
|
||||
|
||||
AnyScalar(const AnyScalar& o);
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar(AnyScalar&& o);
|
||||
#endif
|
||||
|
||||
inline ~AnyScalar() {clear();}
|
||||
|
||||
inline AnyScalar& operator=(const AnyScalar& o) {
|
||||
AnyScalar(o).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AnyScalar& operator=(T v) {
|
||||
AnyScalar(v).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
inline AnyScalar& operator=(AnyScalar&& o) {
|
||||
clear();
|
||||
swap(o);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Reset internal state.
|
||||
//! @version Added after 7.0.0
|
||||
//! @post empty()==true
|
||||
void clear();
|
||||
|
||||
void swap(AnyScalar& o);
|
||||
|
||||
//! Type code of contained value. Or (ScalarType)-1 is empty.
|
||||
inline ScalarType type() const {
|
||||
return _stype;
|
||||
}
|
||||
|
||||
inline void* unsafe() { return _wrap.blob; }
|
||||
inline const void* unsafe() const { return _wrap.blob; }
|
||||
|
||||
inline bool empty() const { return _stype==(ScalarType)-1; }
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
explicit operator bool() const { return !empty(); }
|
||||
#else
|
||||
private:
|
||||
typedef void (AnyScalar::*bool_type)(AnyScalar&);
|
||||
public:
|
||||
operator bool_type() const { return !empty() ? &AnyScalar::swap : 0; }
|
||||
#endif
|
||||
|
||||
//! Provide read-only access to underlying buffer.
|
||||
//! For a string this is std::string::c_str().
|
||||
//! @version Added after 7.0.0
|
||||
const void* bufferUnsafe() const;
|
||||
|
||||
/** Return typed reference to wrapped value. Non-const reference allows value modification
|
||||
*
|
||||
* @throws bad_cast when the requested type does not match the stored type
|
||||
@code
|
||||
AnyScalar v(42);
|
||||
v.ref<uint32>() = 43;
|
||||
assert(v.ref<uint32>() == 43);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
// T -> strip_const -> map to storage type -> add reference
|
||||
typename detail::any_storage_type<typename meta::strip_const<T>::type>::type&
|
||||
ref() {
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
|
||||
throw bad_cast();
|
||||
return reinterpret_cast<TT&>(_wrap.blob);
|
||||
}
|
||||
|
||||
/** Return typed reference to wrapped value. Const reference does not allow modification.
|
||||
*
|
||||
* @throws bad_cast when the requested type does not match the stored type
|
||||
@code
|
||||
const AnyScalar v(42);
|
||||
assert(v.ref<uint32>() == 42);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
// T -> strip_const -> map to storage type -> add const reference
|
||||
typename meta::decorate_const<typename detail::any_storage_type<typename meta::strip_const<T>::type>::type>::type&
|
||||
ref() const {
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
|
||||
throw bad_cast();
|
||||
return reinterpret_cast<typename meta::decorate_const<TT>::type&>(_wrap.blob);
|
||||
}
|
||||
|
||||
/** copy out wrapped value, with a value conversion.
|
||||
*
|
||||
* @throws bad_cast when empty()==true
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
T as() const {
|
||||
typedef typename meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype==(ScalarType)-1)
|
||||
throw bad_cast();
|
||||
TT ret;
|
||||
castUnsafeV(1, (ScalarType)ScalarTypeID<T2>::value, &ret,
|
||||
_stype, _wrap.blob);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
|
||||
};
|
||||
|
||||
epicsShareExtern
|
||||
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // PV_ANYSCALAR_H
|
||||
@@ -1,832 +0,0 @@
|
||||
/* byteBuffer.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
#ifndef BYTEBUFFER_H
|
||||
#define BYTEBUFFER_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <epicsEndian.h>
|
||||
#include <shareLib.h>
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#include <pv/templateMeta.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
|
||||
#ifndef EPICS_ALWAYS_INLINE
|
||||
# define EPICS_ALWAYS_INLINE inline
|
||||
#endif
|
||||
|
||||
/* various compilers provide builtins for byte order swaps.
|
||||
* conditions based on boost endian library
|
||||
*/
|
||||
#if defined(__clang__)
|
||||
|
||||
#if __has_builtin(__builtin_bswap16)
|
||||
#define _PVA_swap16(X) __builtin_bswap16(X)
|
||||
#endif
|
||||
#if __has_builtin(__builtin_bswap32)
|
||||
#define _PVA_swap32(X) __builtin_bswap32(X)
|
||||
#endif
|
||||
#if __has_builtin(__builtin_bswap64)
|
||||
#define _PVA_swap64(X) __builtin_bswap64(X)
|
||||
#endif
|
||||
|
||||
#elif defined(__GNUC__) && ((__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=3))
|
||||
|
||||
#if (__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=8)
|
||||
#define _PVA_swap16(X) __builtin_bswap16(X)
|
||||
#endif
|
||||
|
||||
#define _PVA_swap32(X) __builtin_bswap32(X)
|
||||
#define _PVA_swap64(X) __builtin_bswap64(X)
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
#define _PVA_swap16(X) _byteswap_ushort(X)
|
||||
#define _PVA_swap32(X) _byteswap_ulong(X)
|
||||
#define _PVA_swap64(X) _byteswap_uint64(X)
|
||||
|
||||
#endif
|
||||
|
||||
namespace epics {namespace pvData {
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
struct asInt {
|
||||
static EPICS_ALWAYS_INLINE T from(T v) { return v; }
|
||||
static EPICS_ALWAYS_INLINE T to(T v) { return v; }
|
||||
};
|
||||
template<>
|
||||
struct asInt<float> {
|
||||
union pun {float f; uint32 i;};
|
||||
static EPICS_ALWAYS_INLINE float from(uint32 v) {
|
||||
pun P;
|
||||
P.i = v;
|
||||
return P.f;
|
||||
}
|
||||
static EPICS_ALWAYS_INLINE uint32 to(float v) {
|
||||
pun P;
|
||||
P.f = v;
|
||||
return P.i;
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct asInt<double> {
|
||||
union pun {double f; uint64 i;};
|
||||
static EPICS_ALWAYS_INLINE double from(uint64 v) {
|
||||
pun P;
|
||||
P.i = v;
|
||||
return P.f;
|
||||
}
|
||||
static EPICS_ALWAYS_INLINE uint64 to(double v) {
|
||||
pun P;
|
||||
P.f = v;
|
||||
return P.i;
|
||||
}
|
||||
};
|
||||
|
||||
template<int N>
|
||||
struct swap; // no default
|
||||
template<>
|
||||
struct swap<1> {
|
||||
static EPICS_ALWAYS_INLINE uint8 op(uint8 v) { return v; }
|
||||
};
|
||||
template<>
|
||||
struct swap<2> {
|
||||
static EPICS_ALWAYS_INLINE uint16 op(uint16 v) {
|
||||
#ifdef _PVA_swap16
|
||||
return _PVA_swap16(v);
|
||||
#else
|
||||
return (((v) >> 8) | ((v) << 8));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct swap<4> {
|
||||
static EPICS_ALWAYS_INLINE uint32 op(uint32 v) {
|
||||
#ifdef _PVA_swap32
|
||||
return _PVA_swap32(v);
|
||||
#else
|
||||
return ((((v) & 0xff000000) >> 24) |
|
||||
(((v) & 0x00ff0000) >> 8) |
|
||||
(((v) & 0x0000ff00) << 8) |
|
||||
(((v) & 0x000000ff) << 24));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct swap<8> {
|
||||
#ifdef _PVA_swap64
|
||||
static EPICS_ALWAYS_INLINE uint64 op(uint64 v) {
|
||||
return _PVA_swap64(v);
|
||||
}
|
||||
#else
|
||||
static inline uint64 op(uint64 v) {
|
||||
return (((v) >> 56) | \
|
||||
(((v) >> 40) & 0x0000ff00) | \
|
||||
(((v) >> 24) & 0x00ff0000) | \
|
||||
(((v) >> 8) & 0xff000000) | \
|
||||
(((v) << 8) & ((uint64_t)0xff << 32)) | \
|
||||
(((v) << 24) & ((uint64_t)0xff << 40)) | \
|
||||
(((v) << 40) & ((uint64_t)0xff << 48)) | \
|
||||
(((v) << 56)));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#undef _PVA_swap16
|
||||
#undef _PVA_swap32
|
||||
#undef _PVA_swap64
|
||||
|
||||
/* PVD serialization doesn't pay attention to alignement,
|
||||
* which some targets really care about and treat unaligned
|
||||
* access as a fault, or with a heavy penalty (~= to a syscall).
|
||||
*
|
||||
* For those targets,, we will have to live with the increase
|
||||
* in execution time and/or object code size of byte-wise copy.
|
||||
*/
|
||||
|
||||
#ifdef _ARCH_PPC
|
||||
|
||||
template<typename T>
|
||||
union alignu {
|
||||
T val;
|
||||
char bytes[sizeof(T)];
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
EPICS_ALWAYS_INLINE void store_unaligned(char *buf, T val)
|
||||
{
|
||||
alignu<T> A;
|
||||
A.val = val;
|
||||
for(unsigned i=0, N=sizeof(T); i<N; i++) {
|
||||
buf[i] = A.bytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
EPICS_ALWAYS_INLINE T load_unaligned(const char *buf)
|
||||
{
|
||||
alignu<T> A;
|
||||
for(unsigned i=0, N=sizeof(T); i<N; i++) {
|
||||
A.bytes[i] = buf[i];
|
||||
}
|
||||
return A.val;
|
||||
}
|
||||
|
||||
#else /* alignement */
|
||||
|
||||
template<typename T>
|
||||
EPICS_ALWAYS_INLINE void store_unaligned(char *buf, T val)
|
||||
{
|
||||
*reinterpret_cast<T*>(buf) = val;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
EPICS_ALWAYS_INLINE T load_unaligned(const char *buf)
|
||||
{
|
||||
return *reinterpret_cast<const T*>(buf);
|
||||
}
|
||||
|
||||
#endif /* alignement */
|
||||
|
||||
} // namespace detail
|
||||
|
||||
//! Unconditional byte order swap.
|
||||
//! defined for integer and floating point types
|
||||
template<typename T>
|
||||
EPICS_ALWAYS_INLINE T swap(T val)
|
||||
{
|
||||
return detail::asInt<T>::from(
|
||||
detail::swap<sizeof(T)>::op(
|
||||
detail::asInt<T>::to(val)));
|
||||
}
|
||||
|
||||
#define is_aligned(POINTER, BYTE_COUNT) \
|
||||
(((std::size_t)(POINTER)) % (BYTE_COUNT) == 0)
|
||||
|
||||
#if defined (__GNUC__) && (__GNUC__ < 3)
|
||||
#define GET(T) get((T*)0)
|
||||
#else
|
||||
#define GET(T) get<T>()
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief This class implements a Bytebuffer that is like the java.nio.ByteBuffer.
|
||||
*
|
||||
* <p>A @c BitSet is not safe for multithreaded use without
|
||||
* external synchronization.
|
||||
*
|
||||
* Based on Java implementation.
|
||||
*/
|
||||
class ByteBuffer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param size The number of bytes.
|
||||
* @param byteOrder The byte order.
|
||||
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG.
|
||||
*/
|
||||
ByteBuffer(std::size_t size, int byteOrder = EPICS_BYTE_ORDER) :
|
||||
_buffer((char*)std::malloc(size)), _size(size),
|
||||
_reverseEndianess(byteOrder != EPICS_BYTE_ORDER),
|
||||
_reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER),
|
||||
_wrapped(false)
|
||||
{
|
||||
if(!_buffer)
|
||||
throw std::bad_alloc();
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for wrapping an existing buffer.
|
||||
* Given buffer will not be released by the ByteBuffer instance.
|
||||
* @param buffer Existing buffer. May not be NULL.
|
||||
* @param size The number of bytes.
|
||||
* @param byteOrder The byte order.
|
||||
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG.
|
||||
*/
|
||||
ByteBuffer(char* buffer, std::size_t size, int byteOrder = EPICS_BYTE_ORDER) :
|
||||
_buffer(buffer), _size(size),
|
||||
_reverseEndianess(byteOrder != EPICS_BYTE_ORDER),
|
||||
_reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER),
|
||||
_wrapped(true)
|
||||
{
|
||||
if(!_buffer)
|
||||
throw std::invalid_argument("ByteBuffer can't be constructed with NULL");
|
||||
clear();
|
||||
}
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~ByteBuffer()
|
||||
{
|
||||
if (_buffer && !_wrapped) std::free(_buffer);
|
||||
}
|
||||
/**
|
||||
* Set the byte order.
|
||||
*
|
||||
* @param byteOrder The byte order.
|
||||
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG,
|
||||
*/
|
||||
inline void setEndianess(int byteOrder)
|
||||
{
|
||||
_reverseEndianess = (byteOrder != EPICS_BYTE_ORDER);
|
||||
_reverseFloatEndianess = (byteOrder != EPICS_FLOAT_WORD_ORDER);
|
||||
}
|
||||
/**
|
||||
* Get the raw buffer data.
|
||||
* @return the raw buffer data.
|
||||
*/
|
||||
inline const char* getBuffer() const
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
/**
|
||||
* Makes a buffer ready for a new sequence of channel-read or relative put operations:
|
||||
* It sets the limit to the capacity and the position to zero.
|
||||
*/
|
||||
inline void clear()
|
||||
{
|
||||
_position = _buffer;
|
||||
_limit = _buffer + _size;
|
||||
}
|
||||
/**
|
||||
* Makes a buffer ready to read out previously written values.
|
||||
*
|
||||
* Typically _limit==_buffer+_size is the initial state, but this is not
|
||||
* required.
|
||||
*
|
||||
* V _buffer V _position V _limit V _buffer+_size
|
||||
* |_______written_______|____uninitialized___|____allocated___|
|
||||
*
|
||||
* becomes
|
||||
*
|
||||
* V _buffer/_position V _limit V _buffer+size
|
||||
* |_______written_______|________________allocated____________|
|
||||
*/
|
||||
inline void flip() {
|
||||
_limit = _position;
|
||||
_position = _buffer;
|
||||
}
|
||||
/**
|
||||
* Makes a buffer ready for re-reading the data that it already contains:
|
||||
* It leaves the limit unchanged and sets the position to zero.
|
||||
*
|
||||
* Note that this may allow reading of uninitialized values. flip() should be considered
|
||||
*
|
||||
* V _buffer V _position V _limit V _buffer+_size
|
||||
* |_______written_______|____uninitialized___|____allocated___|
|
||||
*
|
||||
* becomes
|
||||
*
|
||||
* V _buffer/_position V _limit V _buffer+size
|
||||
* |_______written_______|____uninitialized___|____allocated___|
|
||||
*/
|
||||
inline void rewind() {
|
||||
_position = _buffer;
|
||||
}
|
||||
/**
|
||||
* Returns the current position.
|
||||
* @return The current position in the raw data.
|
||||
*/
|
||||
inline std::size_t getPosition() const
|
||||
{
|
||||
return _position - _buffer;
|
||||
}
|
||||
/**
|
||||
* Sets the buffer position.
|
||||
* If the mark is defined and larger than the new position then it is discarded.
|
||||
*
|
||||
* @param pos The offset into the raw buffer.
|
||||
* The new position value; must be no larger than the current limit
|
||||
*/
|
||||
inline void setPosition(std::size_t pos)
|
||||
{
|
||||
assert(pos<=_size);
|
||||
_position = _buffer + pos;
|
||||
assert(_position<=_limit);
|
||||
}
|
||||
/**
|
||||
* Returns this buffer's limit.
|
||||
*
|
||||
* @return The offset into the raw buffer.
|
||||
*/
|
||||
inline std::size_t getLimit() const
|
||||
{
|
||||
return _limit - _buffer;
|
||||
}
|
||||
/**
|
||||
* Sets this buffer's limit.
|
||||
* If the position is larger than the new limit then it is set to the new limit.s
|
||||
* If the mark is defined and larger than the new limit then it is discarded.
|
||||
*
|
||||
* @param limit The new position value;
|
||||
* must be no larger than the current limit
|
||||
*/
|
||||
inline void setLimit(std::size_t limit)
|
||||
{
|
||||
assert(limit<=_size);
|
||||
_limit = _buffer + limit;
|
||||
assert(_position<=_limit);
|
||||
}
|
||||
/**
|
||||
* Returns the number of elements between the current position and the limit.
|
||||
*
|
||||
* @return The number of elements remaining in this buffer.
|
||||
*/
|
||||
inline std::size_t getRemaining() const
|
||||
{
|
||||
return _limit - _position;
|
||||
}
|
||||
/**
|
||||
* Returns The size, i.e. capacity of the raw data buffer in bytes.
|
||||
*
|
||||
* @return The size of the raw data buffer.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE std::size_t getSize() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
/**
|
||||
* Put the value into the raw buffer as a byte stream in the current byte order.
|
||||
*
|
||||
* @param value The value to be put into the byte buffer.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void put(T value);
|
||||
/**
|
||||
* Put the value into the raw buffer at the specified index as a byte stream in the current byte order.
|
||||
*
|
||||
* @param index Offset in the byte buffer.
|
||||
* @param value The value to be put into the byte buffer.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void put(std::size_t index, T value) const;
|
||||
/**
|
||||
* Get the new object from the byte buffer. The item MUST have type @c T.
|
||||
* The position is adjusted based on the type.
|
||||
*
|
||||
* @return The object.
|
||||
*/
|
||||
#if defined (__GNUC__) && (__GNUC__ < 3)
|
||||
template<typename T>
|
||||
inline T get(const T*);
|
||||
#else
|
||||
template<typename T>
|
||||
inline T get();
|
||||
#endif
|
||||
/**
|
||||
* Get the new object from the byte buffer at the specified index.
|
||||
* The item MUST have type @c T.
|
||||
* The position is adjusted based on the type.
|
||||
*
|
||||
* @param index The location in the byte buffer.
|
||||
* @return The object.
|
||||
*/
|
||||
template<typename T>
|
||||
inline T get(std::size_t index) const;
|
||||
/**
|
||||
* Put a sub-array of bytes into the byte buffer.
|
||||
* The position is increased by the count.
|
||||
*
|
||||
* @param src The source array.
|
||||
* @param src_offset The starting position within src.
|
||||
* @param count The number of bytes to put into the byte buffer.
|
||||
* Must be less than getRemaining()
|
||||
*/
|
||||
inline void put(const char* src, std::size_t src_offset, std::size_t count) {
|
||||
assert(count<=getRemaining());
|
||||
memcpy(_position, src + src_offset, count);
|
||||
_position += count;
|
||||
}
|
||||
/**
|
||||
* Get a sub-array of bytes from the byte buffer.
|
||||
* The position is increased by the count.
|
||||
*
|
||||
* @param dest The destination array.
|
||||
* @param dest_offset The starting position within src.
|
||||
* @param count The number of bytes to put into the byte buffer.
|
||||
* Must be less than getRemaining()
|
||||
*/
|
||||
inline void get(char* dest, std::size_t dest_offset, std::size_t count) {
|
||||
assert(count<=getRemaining());
|
||||
memcpy(dest + dest_offset, _position, count);
|
||||
_position += count;
|
||||
}
|
||||
/**
|
||||
* Put an array of type @c T into the byte buffer.
|
||||
* The position is adjusted.
|
||||
*
|
||||
* @param values The input array.
|
||||
* @param count The number of elements.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void putArray(const T* values, std::size_t count);
|
||||
/**
|
||||
* Get an array of type @c T from the byte buffer.
|
||||
* The position is adjusted.
|
||||
*
|
||||
* @param values The destination array.
|
||||
* @param count The number of elements.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void getArray(T* values, std::size_t count);
|
||||
/**
|
||||
* Is the byte order the EPICS_BYTE_ORDER
|
||||
* @return (false,true) if (is, is not) the EPICS_BYTE_ORDER
|
||||
*/
|
||||
template<typename T>
|
||||
EPICS_ALWAYS_INLINE bool reverse() const
|
||||
{
|
||||
return sizeof(T)>1 && _reverseEndianess;
|
||||
}
|
||||
/**
|
||||
* Adjust position to the next multiple of 'size.
|
||||
* @param size The alignment requirement, must be a power of 2. (unchecked)
|
||||
* @param fill value to use for padding bytes (default '\0').
|
||||
*
|
||||
* @note This alignment is absolute, not necessarily with respect to _buffer.
|
||||
*/
|
||||
inline void align(std::size_t size, char fill='\0')
|
||||
{
|
||||
const std::size_t k = size - 1, bufidx = (std::size_t)_position;
|
||||
if(bufidx&k) {
|
||||
std::size_t npad = size-(bufidx&k);
|
||||
assert(npad<=getRemaining());
|
||||
std::fill(_position, _position+npad, fill);
|
||||
_position += npad;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Put a boolean value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putBoolean( bool value) { put< int8>(value ? 1 : 0); }
|
||||
/**
|
||||
* Put a byte value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putByte ( int8 value) { put< int8>(value); }
|
||||
/**
|
||||
* Put a short value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putShort ( int16 value) { put< int16>(value); }
|
||||
/**
|
||||
* Put an int value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putInt ( int32 value) { put< int32>(value); }
|
||||
/**
|
||||
* Put a long value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putLong ( int64 value) { put< int64>(value); }
|
||||
/**
|
||||
* Put a float value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putFloat ( float value) { put< float>(value); }
|
||||
/**
|
||||
* Put a double value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putDouble (double value) { put<double>(value); }
|
||||
|
||||
/**
|
||||
* Put a boolean value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putBoolean(std::size_t index, bool value) { put< int8>(index, value); }
|
||||
/**
|
||||
* Put a byte value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putByte (std::size_t index, int8 value) { put< int8>(index, value); }
|
||||
/**
|
||||
* Put a short value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putShort (std::size_t index, int16 value) { put< int16>(index, value); }
|
||||
/**
|
||||
* Put an int value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putInt (std::size_t index, int32 value) { put< int32>(index, value); }
|
||||
/**
|
||||
* Put a long value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putLong (std::size_t index, int64 value) { put< int64>(index, value); }
|
||||
/**
|
||||
* Put a float value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putFloat (std::size_t index, float value) { put< float>(index, value); }
|
||||
/**
|
||||
* Put a double value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE void putDouble (std::size_t index, double value) { put<double>(index, value); }
|
||||
/**
|
||||
* Get a boolean value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE bool getBoolean() { return GET( int8) != 0; }
|
||||
/**
|
||||
* Get a byte value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE int8 getByte () { return GET( int8); }
|
||||
/**
|
||||
* Get a short value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE int16 getShort () { return GET( int16); }
|
||||
/**
|
||||
* Get a int value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE int32 getInt () { return GET( int32); }
|
||||
/**
|
||||
* Get a long value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE int64 getLong () { return GET( int64); }
|
||||
/**
|
||||
* Get a float value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE float getFloat () { return GET( float); }
|
||||
/**
|
||||
* Get a double value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE double getDouble () { return GET(double); }
|
||||
/**
|
||||
* Get a boolean value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE bool getBoolean(std::size_t index) { return get< int8>(index) != 0; }
|
||||
/**
|
||||
* Get a byte value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE int8 getByte (std::size_t index) { return get< int8>(index); }
|
||||
/**
|
||||
* Get a short value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE int16 getShort (std::size_t index) { return get< int16>(index); }
|
||||
/**
|
||||
* Get an int value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE int32 getInt (std::size_t index) { return get< int32>(index); }
|
||||
/**
|
||||
* Get a long value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE int64 getLong (std::size_t index) { return get< int64>(index); }
|
||||
/**
|
||||
* Get a float value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE float getFloat (std::size_t index) { return get< float>(index); }
|
||||
/**
|
||||
* Get a boolean value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
EPICS_ALWAYS_INLINE double getDouble (std::size_t index) { return get<double>(index); }
|
||||
|
||||
// TODO remove
|
||||
EPICS_ALWAYS_INLINE const char* getArray() const
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
char* const _buffer;
|
||||
char* _position;
|
||||
char* _limit;
|
||||
const std::size_t _size;
|
||||
bool _reverseEndianess;
|
||||
bool _reverseFloatEndianess;
|
||||
const bool _wrapped;
|
||||
};
|
||||
|
||||
template<>
|
||||
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<bool>() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<int8>() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<uint8>() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<float>() const
|
||||
{
|
||||
return _reverseFloatEndianess;
|
||||
}
|
||||
|
||||
template<>
|
||||
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<double>() const
|
||||
{
|
||||
return _reverseFloatEndianess;
|
||||
}
|
||||
|
||||
// the following methods must come after the specialized reverse<>() methods to make pre-gcc3 happy
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::put(T value)
|
||||
{
|
||||
assert(sizeof(T)<=getRemaining());
|
||||
|
||||
if(reverse<T>())
|
||||
value = swap<T>(value);
|
||||
|
||||
detail::store_unaligned(_position, value);
|
||||
_position += sizeof(T);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::put(std::size_t index, T value) const
|
||||
{
|
||||
assert(_buffer+index<=_limit);
|
||||
|
||||
if(reverse<T>())
|
||||
value = swap<T>(value);
|
||||
|
||||
detail::store_unaligned(_buffer+index, value);
|
||||
}
|
||||
|
||||
#if defined (__GNUC__) && (__GNUC__ < 3)
|
||||
template<typename T>
|
||||
inline T ByteBuffer::get(const T*)
|
||||
#else
|
||||
template<typename T>
|
||||
inline T ByteBuffer::get()
|
||||
#endif
|
||||
{
|
||||
assert(sizeof(T)<=getRemaining());
|
||||
|
||||
T value = detail::load_unaligned<T>(_position);
|
||||
_position += sizeof(T);
|
||||
|
||||
if(reverse<T>())
|
||||
value = swap<T>(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T ByteBuffer::get(std::size_t index) const
|
||||
{
|
||||
assert(_buffer+index<=_limit);
|
||||
|
||||
T value = detail::load_unaligned<T>(_buffer + index);
|
||||
|
||||
if(reverse<T>())
|
||||
value = swap<T>(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::putArray(const T* values, std::size_t count)
|
||||
{
|
||||
size_t n = sizeof(T)*count; // bytes
|
||||
assert(n<=getRemaining());
|
||||
|
||||
if (reverse<T>()) {
|
||||
for(std::size_t i=0; i<count; i++) {
|
||||
detail::store_unaligned(_position+i*sizeof(T), swap<T>(values[i]));
|
||||
}
|
||||
} else {
|
||||
memcpy(_position, values, n);
|
||||
}
|
||||
_position += n;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::getArray(T* values, std::size_t count)
|
||||
{
|
||||
size_t n = sizeof(T)*count; // bytes
|
||||
assert(n<=getRemaining());
|
||||
|
||||
if (reverse<T>()) {
|
||||
for(std::size_t i=0; i<count; i++) {
|
||||
values[i] = swap<T>(detail::load_unaligned<T>(_position+i*sizeof(T)));
|
||||
}
|
||||
} else {
|
||||
memcpy(values, _position, n);
|
||||
}
|
||||
_position += n;
|
||||
}
|
||||
|
||||
}}
|
||||
#endif /* BYTEBUFFER_H */
|
||||
@@ -1,328 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/* Author: Michael Davidsaver */
|
||||
/* wrapper around shared_ptr which tracks backwards references.
|
||||
* Can help to find ref. leaks, loops, and other exciting bugs.
|
||||
* See comments in sharedPtr.h
|
||||
*/
|
||||
#ifndef DEBUGPTR_H
|
||||
#define DEBUGPTR_H
|
||||
|
||||
#if __cplusplus<201103L
|
||||
# error c++11 required
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
//! User code should test this macro
|
||||
//! before calling epics::debug::shared_ptr::show_refs()
|
||||
#define HAVE_SHOW_REFS
|
||||
|
||||
namespace epics {
|
||||
namespace debug {
|
||||
|
||||
struct tracker;
|
||||
class shared_ptr_base;
|
||||
|
||||
class epicsShareClass ptr_base {
|
||||
friend class shared_ptr_base;
|
||||
template<typename A>
|
||||
friend class shared_ptr;
|
||||
template<typename A>
|
||||
friend class weak_ptr;
|
||||
protected:
|
||||
typedef std::shared_ptr<tracker> track_t;
|
||||
track_t track;
|
||||
|
||||
ptr_base() noexcept : track() {}
|
||||
ptr_base(const track_t& track) :track(track) {}
|
||||
ptr_base(const ptr_base&) = delete;
|
||||
ptr_base(ptr_base&&) = delete;
|
||||
|
||||
ptr_base& operator=(const ptr_base&) = delete;
|
||||
|
||||
public:
|
||||
typedef std::set<const shared_ptr_base *> ref_set_t;
|
||||
void show_refs(std::ostream&, bool self=true, bool weak=false) const;
|
||||
void spy_refs(ref_set_t&) const;
|
||||
};
|
||||
|
||||
class epicsShareClass weak_ptr_base : public ptr_base {
|
||||
protected:
|
||||
weak_ptr_base() {}
|
||||
weak_ptr_base(const track_t& track) :ptr_base(track) {}
|
||||
};
|
||||
|
||||
class epicsShareClass shared_ptr_base : public ptr_base {
|
||||
protected:
|
||||
shared_ptr_base() noexcept
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
:m_stack(), m_depth(0)
|
||||
#endif
|
||||
{}
|
||||
shared_ptr_base(const track_t& track) :ptr_base(track)
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
,m_stack(), m_depth(0)
|
||||
#endif
|
||||
{}
|
||||
~shared_ptr_base() {track_clear();}
|
||||
|
||||
// add ourselves to tracker
|
||||
void track_new();
|
||||
// create new tracker if ptr!=nullptr, otherwise clear
|
||||
void track_new(void* ptr);
|
||||
// copy tracker and add ourself
|
||||
void track_assign(const shared_ptr_base& o);
|
||||
void track_clear();
|
||||
void swap(shared_ptr_base& o);
|
||||
void snap_stack();
|
||||
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
void *m_stack[EXCEPT_DEPTH];
|
||||
int m_depth; // always <= EXCEPT_DEPTH
|
||||
#endif
|
||||
|
||||
public:
|
||||
void show_stack(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
class shared_ptr;
|
||||
template<typename T>
|
||||
class weak_ptr;
|
||||
template<class Base>
|
||||
class enable_shared_from_this;
|
||||
|
||||
template<typename Store, typename Actual>
|
||||
inline void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
);
|
||||
|
||||
template<typename T>
|
||||
inline void
|
||||
do_enable_shared_from_this(const shared_ptr<T>&, ...) {}
|
||||
|
||||
template<typename T>
|
||||
class shared_ptr : public shared_ptr_base {
|
||||
typedef ::std::shared_ptr<T> real_type;
|
||||
|
||||
real_type real;
|
||||
|
||||
template<typename A>
|
||||
friend class shared_ptr;
|
||||
template<typename A>
|
||||
friend class weak_ptr;
|
||||
|
||||
// ctor for casts
|
||||
shared_ptr(const real_type& r, const ptr_base::track_t& t)
|
||||
:shared_ptr_base(t), real(r)
|
||||
{track_new();}
|
||||
public:
|
||||
typedef typename real_type::element_type element_type;
|
||||
typedef weak_ptr<T> weak_type;
|
||||
|
||||
// new NULL
|
||||
shared_ptr() noexcept {}
|
||||
// copy existing same type
|
||||
shared_ptr(const shared_ptr& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
|
||||
// copy existing of implicitly castable type
|
||||
template<typename A>
|
||||
shared_ptr(const shared_ptr<A>& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
|
||||
|
||||
// construct around new pointer
|
||||
template<typename A, class ... Args>
|
||||
explicit shared_ptr(A* a, Args ... args) : shared_ptr_base(), real(a, args...) {
|
||||
track_new(a);
|
||||
do_enable_shared_from_this(*this, a);
|
||||
}
|
||||
|
||||
// make strong ref from weak
|
||||
template<typename A>
|
||||
shared_ptr(const weak_ptr<A>& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
|
||||
|
||||
// takeover from unique_ptr
|
||||
template<typename A>
|
||||
shared_ptr(std::unique_ptr<A>&& a) : shared_ptr_base(), real(a.release()) {track_new();}
|
||||
|
||||
~shared_ptr() {}
|
||||
|
||||
shared_ptr& operator=(const shared_ptr& o) {
|
||||
if(this!=&o) {
|
||||
real = o.real;
|
||||
track_assign(o);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template<typename A>
|
||||
shared_ptr& operator=(const shared_ptr<A>& o) {
|
||||
if(get()!=o.get()) {
|
||||
real = o.real;
|
||||
track_assign(o);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() noexcept { real.reset(); track_clear(); }
|
||||
template<typename A, class ... Args>
|
||||
void reset(A* a, Args ... args)
|
||||
{
|
||||
real.reset(a, args...);
|
||||
track_new(a);
|
||||
do_enable_shared_from_this(*this, a);
|
||||
}
|
||||
void swap(shared_ptr &o) noexcept
|
||||
{
|
||||
if(this!=&o) {
|
||||
real.swap(o.real);
|
||||
shared_ptr_base::swap(o);
|
||||
}
|
||||
}
|
||||
|
||||
// proxy remaining to underlying shared_ptr
|
||||
|
||||
T* get() const noexcept { return real.get(); }
|
||||
typename std::add_lvalue_reference<T>::type operator*() const noexcept { return *real; }
|
||||
T* operator->() const noexcept { return real.get(); }
|
||||
long use_count() const noexcept { return real.use_count(); }
|
||||
bool unique() const noexcept { return real.unique(); }
|
||||
explicit operator bool() const noexcept { return bool(real); }
|
||||
|
||||
bool operator==(const shared_ptr<T>& o) const { return real==o.real; }
|
||||
bool operator!=(const shared_ptr<T>& o) const { return real!=o.real; }
|
||||
bool operator<(const shared_ptr<T>& o) const { return real<o.real; }
|
||||
|
||||
template<typename A>
|
||||
bool owner_before(const shared_ptr<A>& o) { return real.owner_before(o); }
|
||||
template<typename A>
|
||||
bool owner_before(const weak_ptr<A>& o) { return real.owner_before(o); }
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
friend
|
||||
shared_ptr<TO> static_pointer_cast(const shared_ptr<FROM>& src);
|
||||
template<typename TO, typename FROM>
|
||||
friend
|
||||
shared_ptr<TO> const_pointer_cast(const shared_ptr<FROM>& src);
|
||||
template<typename TO, typename FROM>
|
||||
friend
|
||||
shared_ptr<TO> dynamic_pointer_cast(const shared_ptr<FROM>& src);
|
||||
template<typename Store, typename Actual>
|
||||
friend void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
);
|
||||
};
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
shared_ptr<TO> static_pointer_cast(const shared_ptr<FROM>& src) {
|
||||
return shared_ptr<TO>(std::static_pointer_cast<TO>(src.real), src.track);
|
||||
}
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
shared_ptr<TO> const_pointer_cast(const shared_ptr<FROM>& src) {
|
||||
return shared_ptr<TO>(std::const_pointer_cast<TO>(src.real), src.track);
|
||||
}
|
||||
|
||||
template<typename TO, typename FROM>
|
||||
shared_ptr<TO> dynamic_pointer_cast(const shared_ptr<FROM>& src) {
|
||||
return shared_ptr<TO>(std::dynamic_pointer_cast<TO>(src.real), src.track);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class weak_ptr : public weak_ptr_base {
|
||||
typedef ::std::weak_ptr<T> real_type;
|
||||
|
||||
real_type real;
|
||||
|
||||
template<typename A>
|
||||
friend class shared_ptr;
|
||||
template<typename A>
|
||||
friend class weak_ptr;
|
||||
|
||||
public:
|
||||
typedef typename real_type::element_type element_type;
|
||||
typedef weak_ptr<T> weak_type;
|
||||
|
||||
// new NULL
|
||||
weak_ptr() noexcept {}
|
||||
// copy existing same type
|
||||
weak_ptr(const weak_ptr& o) :weak_ptr_base(o.track), real(o.real) {}
|
||||
// copy existing of similar type
|
||||
template<typename A>
|
||||
weak_ptr(const weak_ptr<A>& o) :weak_ptr_base(o.track), real(o.real) {}
|
||||
|
||||
// create week ref from strong ref
|
||||
template<typename A>
|
||||
weak_ptr(const shared_ptr<A>& o) :weak_ptr_base(o.track), real(o.real) {}
|
||||
|
||||
~weak_ptr() {}
|
||||
|
||||
weak_ptr& operator=(const weak_ptr& o) {
|
||||
if(this!=&o) {
|
||||
real = o.real;
|
||||
track = o.track;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template<typename A>
|
||||
weak_ptr& operator=(const shared_ptr<A>& o) {
|
||||
real = o.real;
|
||||
track = o.track;
|
||||
return *this;
|
||||
}
|
||||
|
||||
shared_ptr<T> lock() const noexcept { return shared_ptr<T>(real.lock(), track); }
|
||||
void reset() noexcept { track.reset(); real.reset(); }
|
||||
|
||||
long use_count() const noexcept { return real.use_count(); }
|
||||
bool unique() const noexcept { return real.unique(); }
|
||||
};
|
||||
|
||||
template<class Base>
|
||||
class enable_shared_from_this {
|
||||
mutable weak_ptr<Base> xxInternalSelf;
|
||||
|
||||
template<typename Store, typename Actual>
|
||||
friend
|
||||
void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
);
|
||||
public:
|
||||
shared_ptr<Base> shared_from_this() const {
|
||||
return shared_ptr<Base>(xxInternalSelf);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Store, typename Actual>
|
||||
inline void
|
||||
do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
enable_shared_from_this<Actual>* self
|
||||
)
|
||||
{
|
||||
shared_ptr<Actual> actual(dynamic_pointer_cast<Actual>(dest));
|
||||
if(!actual)
|
||||
throw std::logic_error("epics::debug::enabled_shared_from_this fails");
|
||||
self->xxInternalSelf = actual;
|
||||
}
|
||||
|
||||
}} // namespace epics::debug
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream& operator<<(std::ostream& strm, const epics::debug::shared_ptr<T>& ptr)
|
||||
{
|
||||
strm<<ptr.get();
|
||||
return strm;
|
||||
}
|
||||
|
||||
#endif // DEBUGPTR_H
|
||||
@@ -1,65 +0,0 @@
|
||||
/* noDefaultMethods.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef NO_DEFAULT_METHODS_H
|
||||
#define NO_DEFAULT_METHODS_H
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
/** @macro EPICS_NOT_COPYABLE(CLASS)
|
||||
* @brief Disable implicit copyable
|
||||
*
|
||||
* Prevent the default copy constructor and assignment
|
||||
* operator from being usable.
|
||||
*
|
||||
* For >= C++11 explicitly disable. Attempts to copy/assign will
|
||||
* fail to compile.
|
||||
*
|
||||
* For C++98 make these private, and don't implement them.
|
||||
* User code will fail to compile, implementation code will fail to link.
|
||||
@code
|
||||
struct MyClass {
|
||||
EPICS_NOT_COPYABLE(MyClass)
|
||||
public:
|
||||
...
|
||||
};
|
||||
@endcode
|
||||
*
|
||||
* @note This macro contains 'private:'.
|
||||
*/
|
||||
#if __cplusplus>=201103L
|
||||
# define EPICS_NOT_COPYABLE(CLASS) private: CLASS(const CLASS&) = delete; CLASS& operator=(const CLASS&) = delete;
|
||||
#else
|
||||
# define EPICS_NOT_COPYABLE(CLASS) private: CLASS(const CLASS&); CLASS& operator=(const CLASS&);
|
||||
#endif
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
/**
|
||||
* @brief Base class for not allowing default methods.
|
||||
*
|
||||
* Note that copy constructor a copy methods are declared private.
|
||||
*
|
||||
* @deprecated Deprecated in favor of EPICS_NOT_COPYABLE() pvDataCPP 7.0.0
|
||||
*/
|
||||
class NoDefaultMethods {
|
||||
public:
|
||||
NoDefaultMethods() {}
|
||||
private:
|
||||
#if __cplusplus>=201103L
|
||||
NoDefaultMethods(const NoDefaultMethods&) = delete;
|
||||
NoDefaultMethods & operator=(const NoDefaultMethods &) = delete;
|
||||
#else
|
||||
// do not implement
|
||||
NoDefaultMethods(const NoDefaultMethods&);
|
||||
NoDefaultMethods & operator=(const NoDefaultMethods &);
|
||||
#endif
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* NO_DEFAULT_METHODS_H */
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
#ifndef PVUNITTEST_H
|
||||
#define PVUNITTEST_H
|
||||
|
||||
#include <sstream>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class C, void (C::*M)()>
|
||||
void test_method(const char *kname, const char *mname)
|
||||
{
|
||||
try {
|
||||
testDiag("------- %s::%s --------", kname, mname);
|
||||
C inst;
|
||||
(inst.*M)();
|
||||
} catch(std::exception& e) {
|
||||
PRINT_EXCEPTION(e);
|
||||
testAbort("unexpected exception: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
class epicsShareClass testPassx
|
||||
{
|
||||
std::ostringstream strm;
|
||||
const bool dotest, pass;
|
||||
bool alive;
|
||||
public:
|
||||
testPassx() :dotest(false), pass(false), alive(true) {}
|
||||
explicit testPassx(bool r) :dotest(true), pass(r), alive(true) {}
|
||||
~testPassx();
|
||||
template<typename T>
|
||||
inline testPassx& operator<<(const T& v) {
|
||||
strm<<v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// allow testPassx to be returned
|
||||
// move ctor masquerading as copy ctor
|
||||
testPassx(testPassx& o);
|
||||
private:
|
||||
testPassx& operator=(const testPassx&);
|
||||
};
|
||||
|
||||
template<typename LHS, typename RHS>
|
||||
inline testPassx testEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
|
||||
{
|
||||
return testPassx(l==r)<<nLHS<<" ("<<l<<") == "<<nRHS<<" ("<<r<<")";
|
||||
}
|
||||
|
||||
template<typename LHS, typename RHS>
|
||||
inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
|
||||
{
|
||||
return testPassx(l!=r)<<nLHS<<" ("<<l<<") != "<<nRHS<<" ("<<r<<")";
|
||||
}
|
||||
|
||||
}//namespace detail
|
||||
|
||||
/** @defgroup testhelpers Unit testing helpers
|
||||
*
|
||||
* Helper functions for writing unit tests.
|
||||
*
|
||||
@include unittest.cpp
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Run a class method as a test.
|
||||
*
|
||||
* Each invocation of TEST_METHOD() constructs a new instance of 'klass' on the stack.
|
||||
* Thus constructor and destructor can be used for common test setup and tear down.
|
||||
@code
|
||||
namespace { // anon
|
||||
struct MyTest {
|
||||
MyTest() { } // setup
|
||||
~MyTest() { } // tear down
|
||||
void test1() {}
|
||||
void test2() {}
|
||||
};
|
||||
} // namespace anon
|
||||
MAIN(somename) {
|
||||
testPlan(0);
|
||||
TEST_METHOD(MyTest, test1)
|
||||
TEST_METHOD(MyTest, test2)
|
||||
return testDone();
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
#define TEST_METHOD(klass, method) ::detail::test_method<klass, &klass::method>(#klass, #method)
|
||||
|
||||
/** Compare equality. print left and right hand values and expression strings
|
||||
*
|
||||
@code
|
||||
int x=5;
|
||||
testEqual(x, 5);
|
||||
// prints "ok 1 - x (5) == 5 (5)\n"
|
||||
testEqual(x, 6)<<" oops";
|
||||
// prints "not ok 1 - x (5) == 6 (6) oops\n"
|
||||
@endcode
|
||||
*/
|
||||
#define testEqual(LHS, RHS) ::detail::testEqualx(#LHS, #RHS, LHS, RHS)
|
||||
|
||||
#define testNotEqual(LHS, RHS) ::detail::testNotEqualx(#LHS, #RHS, LHS, RHS)
|
||||
|
||||
/** Pass/fail from boolean
|
||||
*
|
||||
@code
|
||||
bool y=true;
|
||||
testTrue(y);
|
||||
// prints "ok 1 - y\n"
|
||||
testTrue(!y)<<" oops";
|
||||
// prints "not ok 1 - !y oops\n"
|
||||
@endcode
|
||||
*/
|
||||
#define testTrue(B) ::detail::testPassx(!!(B))<<#B
|
||||
|
||||
/** Test that a given block throws an exception
|
||||
*
|
||||
@code
|
||||
testThrows(std::runtime_error, somefunc(5))
|
||||
@endcode
|
||||
*/
|
||||
#define testThrows(EXC, CODE) try{ CODE; testFail("unexpected success of " #CODE); }catch(EXC& e){testPass("catch expected exception: %s", e.what());}
|
||||
|
||||
/** Print test output w/o testing
|
||||
*
|
||||
@code
|
||||
testShow()<<"Foo";
|
||||
@endcode
|
||||
*/
|
||||
#define testShow() ::detail::testPassx()
|
||||
|
||||
/** Compare value of PVStructure field
|
||||
*
|
||||
@code
|
||||
PVStructurePtr x(.....);
|
||||
testFieldEqual<epics::pvData::PVInt>(x, "alarm.severity", 1);
|
||||
@endcode
|
||||
*/
|
||||
template<typename PVD>
|
||||
::detail::testPassx
|
||||
testFieldEqual(const std::tr1::shared_ptr<const epics::pvData::PVStructure>& val, const char *name, typename PVD::value_type expect)
|
||||
{
|
||||
if(!val) {
|
||||
return ::detail::testPassx(false)<<" null structure pointer";
|
||||
}
|
||||
typename PVD::const_shared_pointer fval(val->getSubField<PVD>(name));
|
||||
if(!fval) {
|
||||
epics::pvData::PVUnion::const_shared_pointer uval(val->getSubField<epics::pvData::PVUnion>(name));
|
||||
if(uval)
|
||||
fval = uval->get<PVD>();
|
||||
}
|
||||
if(!fval) {
|
||||
return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
|
||||
} else {
|
||||
typename PVD::value_type actual(fval->get());
|
||||
return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename PVD>
|
||||
::detail::testPassx
|
||||
testFieldEqual(const std::tr1::shared_ptr<const epics::pvData::PVStructure>& val, const char *name, typename PVD::const_svector expect)
|
||||
{
|
||||
if(!val) {
|
||||
return ::detail::testPassx(false)<<" null structure pointer";
|
||||
}
|
||||
typename PVD::const_shared_pointer fval(val->getSubField<PVD>(name));
|
||||
if(!fval) {
|
||||
return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
|
||||
} else {
|
||||
typename PVD::const_svector actual(fval->view());
|
||||
return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif // PVUNITTEST_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user