summaryrefslogtreecommitdiffstats
path: root/win/makefile.bc
diff options
context:
space:
mode:
authordkf <donal.k.fellows@manchester.ac.uk>2003-06-25 23:02:10 (GMT)
committerdkf <donal.k.fellows@manchester.ac.uk>2003-06-25 23:02:10 (GMT)
commit43e3bcc7712f80d3c36696dbc1f9349f2819fe27 (patch)
tree9e458c1fda7815410cef063c83bd16dfb8e14927 /win/makefile.bc
parentb2985d83d53d176dc990188526b12b93860253c7 (diff)
downloadtcl-43e3bcc7712f80d3c36696dbc1f9349f2819fe27.zip
tcl-43e3bcc7712f80d3c36696dbc1f9349f2819fe27.tar.gz
tcl-43e3bcc7712f80d3c36696dbc1f9349f2819fe27.tar.bz2
Factored out the trace code - it's big enough to be its own maintenance area
and tricky enough to discourage non-specialists...
Diffstat (limited to 'win/makefile.bc')
-rw-r--r--win/makefile.bc1
1 files changed, 1 insertions, 0 deletions
diff --git a/win/makefile.bc b/win/makefile.bc
index 54b4600..c2d0ca7 100644
--- a/win/makefile.bc
+++ b/win/makefile.bc
@@ -254,6 +254,7 @@ TCLOBJS = \
$(TMPDIR)\tclThread.obj \
$(TMPDIR)\tclThreadJoin.obj \
$(TMPDIR)\tclTimer.obj \
+ $(TMPDIR)\tclTrace.obj \
$(TMPDIR)\tclUtf.obj \
$(TMPDIR)\tclUtil.obj \
$(TMPDIR)\tclVar.obj \
id='n1070' href='#n1070'>1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * Read-Only S3 Virtual File Driver (VFD)
 *
 * Programmer: Jacob Smith
 *             2017-10-13
 *
 * Purpose:
 *
 *     Provide read-only access to files hosted on Amazon's S3 service.
 *     Relies on "s3comms" utility layer to implement the AWS REST API.
 */

/* This source code file is part of the H5FD driver module */
#include "H5FDdrvr_module.h"

#include "H5private.h"      /* Generic Functions        */
#include "H5Eprivate.h"     /* Error handling           */
#include "H5FDprivate.h"    /* File drivers             */
#include "H5FDros3.h"       /* ros3 file driver         */
#include "H5FLprivate.h"    /* Free Lists               */
#include "H5Iprivate.h"     /* IDs                      */
#include "H5MMprivate.h"    /* Memory management        */
#include "H5FDs3comms.h"    /* S3 Communications        */

#ifdef H5_HAVE_ROS3_VFD

/* toggle function call prints: 1 turns on
 */
#define ROS3_DEBUG 0

/* toggle stats collection and reporting
 */
#define ROS3_STATS 0

/* The driver identification number, initialized at runtime
 */
static hid_t H5FD_ROS3_g = 0;

#if ROS3_STATS

/* arbitrarily large value, such that any reasonable size read will be "less"
 * than this value and set a true minimum
 * not 0 because that may be a valid recorded minimum in degenerate cases
 */
#define ROS3_STATS_STARTING_MIN 0xfffffffful

/* Configuration definitions for stats collection and breakdown
 *
 * 2^10 = 1024
 *     Reads up to 1024 bytes (1 kB) fall in bin 0
 * 2^(10+(1*16)) = 2^26 = 64MB
 *     Reads of 64MB or greater fall in "overflow" bin[BIN_COUNT]
 */
#define ROS3_STATS_BASE         2
#define ROS3_STATS_INTERVAL     1
#define ROS3_STATS_START_POWER 10
#define ROS3_STATS_BIN_COUNT   16 /* MUST BE GREATER THAN 0 */


/*
 * Calculate `BASE ^ (START_POWER + (INTERVAL * bin_i))`
 * Stores result at `(unsigned long long *) out_ptr`.
 * Used in computing boundaries between stats bins.
 */
#define ROS3_STATS_POW(bin_i, out_ptr) {                       \
    unsigned long long donotshadowresult = 1;                  \
    unsigned           donotshadowindex  = 0;                  \
    for (donotshadowindex = 0;                                 \
         donotshadowindex < (((bin_i) * ROS3_STATS_INTERVAL) + \
                              ROS3_STATS_START_POWER);         \
         donotshadowindex++)                                   \
    {                                                          \
        donotshadowresult *= ROS3_STATS_BASE;                  \
    }                                                          \
    *(out_ptr) = donotshadowresult;                            \
}

/* array to hold pre-computed boundaries for stats bins
 */
static unsigned long long ros3_stats_boundaries[ROS3_STATS_BIN_COUNT];

/***************************************************************************
 *
 * Structure: ros3_statsbin
 *
 * Purpose:
 *
 *     Structure for storing per-file ros3 VFD usage statistics.
 *
 *
 *
 * `count` (unsigned long long)
 *
 *     Number of reads with size in this bin's range.
 *
 * `bytes` (unsigned long long)
 *
 *     Total number of bytes read through this bin.
 *
 * `min` (unsigned long long)
 *
 *     Smallest read size in this bin.
 *
 * `max` (unsigned long long)
 *
 *     Largest read size in this bin.
 *
 *
 *
 * Programmer: Jacob Smith
 *
 ***************************************************************************/
typedef struct {
    unsigned long long count;
    unsigned long long bytes;
    unsigned long long min;
    unsigned long long max;
} ros3_statsbin;

#endif /* ROS3_STATS */

/***************************************************************************
 *
 * Structure: H5FD_ros3_t
 *
 * Purpose:
 *
 *     H5FD_ros3_t is a structure used to store all information needed to
 *     maintain R/O access to a single HDF5 file that has been stored as a
 *     S3 object.  This structure is created when such a file is "opened" and
 *     discarded when it is "closed".
 *
 *     Presents an S3 object as a file to the HDF5 library.
 *
 *
 *
 * `pub` (H5FD_t)
 *
 *     Instance of H5FD_t which contains all fields common to all VFDs.
 *     It must be the first item in this structure, since at higher levels,
 *     this structure will be treated as an instance of H5FD_t.
 *
 * `fa` (H5FD_ros3_fapl_t)
 *
 *     Instance of `H5FD_ros3_fapl_t` containing the S3 configuration data
 *     needed to "open" the HDF5 file.
 *
 * `eoa` (haddr_t)
 *
 *     End of addressed space in file. After open, it should always
 *     equal the file size.
 *
 * `s3r_handle` (s3r_t *)
 *
 *     Instance of S3 Request handle associated with the target resource.
 *     Responsible for communicating with remote host and presenting file
 *     contents as indistinguishable from a file on the local filesystem.
 *
 * *** present only if ROS3_SATS is flagged to enable stats collection ***
 *
 * `meta` (ros3_statsbin[])
 * `raw` (ros3_statsbin[])
 *
 *     Only present if ros3 stats collection is enabled.
 *
 *     Arrays of `ros3_statsbin` structures to record raw- and metadata reads.
 *
 *     Records count and size of reads performed by the VFD, and is used to
 *     print formatted usage statistics to stdout upon VFD shutdown.
 *
 *     Reads of each raw- and metadata type are recorded in an individual bin
 *     determined by the size of the read.  The last bin of each type is
 *     reserved for "big" reads, with no defined upper bound.
 *
 * *** end ROS3_STATS ***
 *
 *
 *
 * Programmer: Jacob Smith
 *
 ***************************************************************************/
typedef struct H5FD_ros3_t {
    H5FD_t            pub;
    H5FD_ros3_fapl_t  fa;
    haddr_t           eoa;
    s3r_t            *s3r_handle;
#if ROS3_STATS
    ros3_statsbin    meta[ROS3_STATS_BIN_COUNT + 1];
    ros3_statsbin    raw[ROS3_STATS_BIN_COUNT + 1];
#endif
} H5FD_ros3_t;

/*
 * These macros check for overflow of various quantities.  These macros
 * assume that HDoff_t is signed and haddr_t and size_t are unsigned.
 *
 * ADDR_OVERFLOW:   Checks whether a file address of type `haddr_t'
 *                  is too large to be represented by the second argument
 *                  of the file seek function.
 *                  Only included if it may be used -- ROS3 VFD is enabled.
 *
 */
#define MAXADDR (((haddr_t)1 << (8 * sizeof(HDoff_t) - 1)) - 1)
#define ADDR_OVERFLOW(A)    (HADDR_UNDEF==(A) || ((A) & ~(haddr_t)MAXADDR))

/* Prototypes */
static herr_t  H5FD__ros3_term(void);
static void   *H5FD__ros3_fapl_get(H5FD_t *_file);
static void   *H5FD__ros3_fapl_copy(const void *_old_fa);
static herr_t  H5FD__ros3_fapl_free(void *_fa);
static H5FD_t *H5FD__ros3_open(const char *name, unsigned flags, hid_t fapl_id,
                              haddr_t maxaddr);
static herr_t  H5FD__ros3_close(H5FD_t *_file);
static int     H5FD__ros3_cmp(const H5FD_t *_f1, const H5FD_t *_f2);
static herr_t  H5FD__ros3_query(const H5FD_t *_f1, unsigned long *flags);
static haddr_t H5FD__ros3_get_eoa(const H5FD_t *_file, H5FD_mem_t type);
static herr_t  H5FD__ros3_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr);
static haddr_t H5FD__ros3_get_eof(const H5FD_t *_file, H5FD_mem_t type);
static herr_t  H5FD__ros3_get_handle(H5FD_t *_file, hid_t fapl,
                                    void** file_handle);
static herr_t  H5FD__ros3_read(H5FD_t *_file, H5FD_mem_t type, hid_t fapl_id,
                               haddr_t addr, size_t size, void *buf);
static herr_t  H5FD__ros3_write(H5FD_t *_file, H5FD_mem_t type, hid_t fapl_id,
                               haddr_t addr, size_t size, const void *buf);
static herr_t  H5FD__ros3_truncate(H5FD_t *_file, hid_t dxpl_id,
                                  hbool_t closing);
static herr_t  H5FD__ros3_lock(H5FD_t *_file, hbool_t rw);
static herr_t  H5FD__ros3_unlock(H5FD_t *_file);

static herr_t  H5FD__ros3_validate_config(const H5FD_ros3_fapl_t * fa);

static const H5FD_class_t H5FD_ros3_g = {
    "ros3",                     /* name                 */
    MAXADDR,                    /* maxaddr              */
    H5F_CLOSE_WEAK,             /* fc_degree            */
    H5FD__ros3_term,            /* terminate            */
    NULL,                       /* sb_size              */
    NULL,                       /* sb_encode            */
    NULL,                       /* sb_decode            */
    sizeof(H5FD_ros3_fapl_t),   /* fapl_size            */
    H5FD__ros3_fapl_get,        /* fapl_get             */
    H5FD__ros3_fapl_copy,       /* fapl_copy            */
    H5FD__ros3_fapl_free,       /* fapl_free            */
    0,                          /* dxpl_size            */
    NULL,                       /* dxpl_copy            */
    NULL,                       /* dxpl_free            */
    H5FD__ros3_open,            /* open                 */
    H5FD__ros3_close,           /* close                */
    H5FD__ros3_cmp,             /* cmp                  */
    H5FD__ros3_query,           /* query                */
    NULL,                       /* get_type_map         */
    NULL,                       /* alloc                */
    NULL,                       /* free                 */
    H5FD__ros3_get_eoa,         /* get_eoa              */
    H5FD__ros3_set_eoa,         /* set_eoa              */
    H5FD__ros3_get_eof,         /* get_eof              */
    H5FD__ros3_get_handle,      /* get_handle           */
    H5FD__ros3_read,            /* read                 */
    H5FD__ros3_write,           /* write                */
    NULL,                       /* flush                */
    H5FD__ros3_truncate,        /* truncate             */
    H5FD__ros3_lock,            /* lock                 */
    H5FD__ros3_unlock,          /* unlock               */
    H5FD_FLMAP_DICHOTOMY        /* fl_map               */
};

/* Declare a free list to manage the H5FD_ros3_t struct */
H5FL_DEFINE_STATIC(H5FD_ros3_t);


/*-------------------------------------------------------------------------
 * Function:    H5FD__init_package
 *
 * Purpose:     Initializes any interface-specific data or routines.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 * Programmer:  Jacob Smith 2017
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__init_package(void)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_STATIC

    if(H5FD_ros3_init() < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "unable to initialize ros3 VFD")

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__init_package() */


/*-------------------------------------------------------------------------
 * Function:    H5FD_ros3_init
 *
 * Purpose:     Initialize this driver by registering the driver with the
 *              library.
 *
 * Return:      Success:    The driver ID for the ros3 driver.
 *              Failure:    Negative
 *
 * Programmer:  Jacob Smith 2017
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5FD_ros3_init(void)
{
    hid_t ret_value = H5I_INVALID_HID;
#if ROS3_STATS
    unsigned int bin_i;
#endif

    FUNC_ENTER_NOAPI(FAIL)

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD_ros3_init() called.\n");
#endif

    if(H5I_VFL != H5I_get_type(H5FD_ROS3_g))
        H5FD_ROS3_g = H5FD_register(&H5FD_ros3_g, sizeof(H5FD_class_t), FALSE);

#if ROS3_STATS
    /* pre-compute statsbin boundaries
     */
    for (bin_i = 0; bin_i < ROS3_STATS_BIN_COUNT; bin_i++) {
        unsigned long long value = 0;

        ROS3_STATS_POW(bin_i, &value)
        ros3_stats_boundaries[bin_i] = value;
    }
#endif

    /* Set return value */
    ret_value = H5FD_ROS3_g;

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD_ros3_init() */


/*---------------------------------------------------------------------------
 * Function:    H5FD__ros3_term
 *
 * Purpose:     Shut down the VFD
 *
 * Returns:     SUCCEED (Can't fail)
 *
 * Programmer:  Jacob Smith 2017
 *
 *---------------------------------------------------------------------------
 */
static herr_t
H5FD__ros3_term(void)
{
    FUNC_ENTER_STATIC_NOERR

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_term() called.\n");
#endif

    /* Reset VFL ID */
    H5FD_ROS3_g = 0;

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5FD__ros3_term() */


/*-------------------------------------------------------------------------
 * Function:    H5Pset_fapl_ros3
 *
 * Purpose:     Modify the file access property list to use the H5FD_ROS3
 *              driver defined in this source file.  All driver specfic
 *              properties are passed in as a pointer to a suitably
 *              initialized instance of H5FD_ros3_fapl_t
 *
 * Return:      SUCCEED/FAIL
 *
 * Programmer:  John Mainzer
 *              9/10/17
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Pset_fapl_ros3(hid_t fapl_id, H5FD_ros3_fapl_t *fa)
{
    H5P_genplist_t *plist     = NULL; /* Property list pointer */
    herr_t          ret_value = FAIL;

    FUNC_ENTER_API(FAIL)
    H5TRACE2("e", "i*x", fapl_id, fa);

    HDassert(fa != NULL);

#if ROS3_DEBUG
    HDfprintf(stdout, "H5Pset_fapl_ros3() called.\n");
#endif

    plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS);
    if(plist == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access property list")

    if(FAIL == H5FD__ros3_validate_config(fa))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid ros3 config")

    ret_value = H5P_set_driver(plist, H5FD_ROS3, (void *)fa);

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Pset_fapl_ros3() */


/*-------------------------------------------------------------------------
 * Function:    H5FD__ros3_validate_config()
 *
 * Purpose:     Test to see if the supplied instance of H5FD_ros3_fapl_t
 *              contains internally consistant data.  Return SUCCEED if so,
 *              and FAIL otherwise.
 *
 *              Note the difference between internally consistant and
 *              correct.  As we will have to try to access the target
 *              object to determine whether the supplied data is correct,
 *              we will settle for internal consistancy at this point
 *
 * Return:      SUCCEED if instance of H5FD_ros3_fapl_t contains internally
 *              consistant data, FAIL otherwise.
 *
 * Programmer:  Jacob Smith
 *              9/10/17
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__ros3_validate_config(const H5FD_ros3_fapl_t * fa)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_STATIC

    HDassert(fa != NULL);

    if(fa->version != H5FD_CURR_ROS3_FAPL_T_VERSION)
         HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Unknown H5FD_ros3_fapl_t version");

    /* if set to authenticate, region and id cannot be empty strings */
    if(fa->authenticate == TRUE)
        if((fa->aws_region[0] == '\0') || (fa->secret_id[0]  == '\0'))
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Inconsistent authentication information");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__ros3_validate_config() */


/*-------------------------------------------------------------------------
 * Function:    H5Pget_fapl_ros3
 *
 * Purpose:     Returns information about the ros3 file access property
 *              list though the function arguments.
 *
 * Return:      Success:        Non-negative
 *
 *              Failure:        Negative
 *
 * Programmer:  John Mainzer
 *              9/10/17
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Pget_fapl_ros3(hid_t fapl_id, H5FD_ros3_fapl_t *fa_out)
{
    const H5FD_ros3_fapl_t *fa        = NULL;
    H5P_genplist_t         *plist     = NULL;
    herr_t                  ret_value = SUCCEED;

    FUNC_ENTER_API(FAIL)
    H5TRACE2("e", "i*x", fapl_id, fa_out);

#if ROS3_DEBUG
    HDfprintf(stdout, "H5Pget_fapl_ros3() called.\n");
#endif

    if(fa_out == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "fa_out is NULL")

    plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS);
    if(plist == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access list")

    if(H5FD_ROS3 != H5P_peek_driver(plist))
        HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "incorrect VFL driver")

    fa = (const H5FD_ros3_fapl_t *)H5P_peek_driver_info(plist);
    if(fa == NULL)
        HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "bad VFL driver info")

    /* Copy the ros3 fapl data out */
    HDmemcpy(fa_out, fa, sizeof(H5FD_ros3_fapl_t));

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Pget_fapl_ros3() */


/*-------------------------------------------------------------------------
 * Function:    H5FD__ros3_fapl_get
 *
 * Purpose:     Gets a file access property list which could be used to
 *              create an identical file.
 *
 * Return:      Success:        Ptr to new file access property list value.
 *
 *              Failure:        NULL
 *
 * Programmer:  John Mainzer
 *              9/8/17
 *
 *-------------------------------------------------------------------------
 */
static void *
H5FD__ros3_fapl_get(H5FD_t *_file)
{
    H5FD_ros3_t      *file      = (H5FD_ros3_t*)_file;
    H5FD_ros3_fapl_t *fa        = NULL;
    void             *ret_value = NULL;

    FUNC_ENTER_STATIC

    fa = (H5FD_ros3_fapl_t *)H5MM_calloc(sizeof(H5FD_ros3_fapl_t));
    if(fa == NULL)
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed")

    /* Copy the fields of the structure */
    HDmemcpy(fa, &(file->fa), sizeof(H5FD_ros3_fapl_t));

    /* Set return value */
    ret_value = fa;

done:
    if(ret_value == NULL)
        if(fa != NULL)
            H5MM_xfree(fa);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__ros3_fapl_get() */


/*-------------------------------------------------------------------------
 * Function:    H5FD__ros3_fapl_copy
 *
 * Purpose:     Copies the ros3-specific file access properties.
 *
 * Return:      Success:        Ptr to a new property list
 *
 *              Failure:        NULL
 *
 * Programmer:  John Mainzer
 *              9/8/17
 *
 *-------------------------------------------------------------------------
 */
static void *
H5FD__ros3_fapl_copy(const void *_old_fa)
{
    const H5FD_ros3_fapl_t *old_fa    = (const H5FD_ros3_fapl_t*)_old_fa;
    H5FD_ros3_fapl_t       *new_fa    = NULL;
    void                   *ret_value = NULL;

    FUNC_ENTER_STATIC

    new_fa = (H5FD_ros3_fapl_t *)H5MM_malloc(sizeof(H5FD_ros3_fapl_t));
    if(new_fa == NULL)
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");

    HDmemcpy(new_fa, old_fa, sizeof(H5FD_ros3_fapl_t));
    ret_value = new_fa;

done:
    if(ret_value == NULL)
        if(new_fa != NULL)
            H5MM_xfree(new_fa);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__ros3_fapl_copy() */


/*-------------------------------------------------------------------------
 * Function:    H5FD__ros3_fapl_free
 *
 * Purpose:     Frees the ros3-specific file access properties.
 *
 * Return:      SUCCEED (cannot fail)
 *
 * Programmer:  John Mainzer
 *              9/8/17
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__ros3_fapl_free(void *_fa)
{
    H5FD_ros3_fapl_t *fa = (H5FD_ros3_fapl_t*)_fa;

    FUNC_ENTER_STATIC_NOERR

    HDassert(fa != NULL); /* sanity check */

    H5MM_xfree(fa);

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5FD__ros3_fapl_free() */

#if ROS3_STATS
/*----------------------------------------------------------------------------
 *
 * Function: ros3_reset_stats()
 *
 * Purpose:
 *
 *     Reset the stats collection elements in this virtual file structure.
 *
 *     Clears any set data in stats bins; initializes/zeroes values.
 *
 * Return:
 *
 *     - SUCCESS: `SUCCEED`
 *     - FAILURE: `FAIL`
 *         - Occurs if the file is invalid somehow
 *
 * Programmer: Jacob Smith
 *             2017-12-08
 *
 *----------------------------------------------------------------------------
 */
static herr_t
ros3_reset_stats(H5FD_ros3_t *file)
{
    unsigned i         = 0;
    herr_t   ret_value = SUCCEED;

    FUNC_ENTER_STATIC

#if ROS3_DEBUG
    HDprintf("ros3_reset_stats() called\n");
#endif

    if(file == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "file was null");

    for(i = 0; i <= ROS3_STATS_BIN_COUNT; i++) {
        file->raw[i].bytes  = 0;
        file->raw[i].count  = 0;
        file->raw[i].min    = (unsigned long long)ROS3_STATS_STARTING_MIN;
        file->raw[i].max    = 0;

        file->meta[i].bytes = 0;
        file->meta[i].count = 0;
        file->meta[i].min   = (unsigned long long)ROS3_STATS_STARTING_MIN;
        file->meta[i].max   = 0;
    }

done:
    FUNC_LEAVE_NOAPI(ret_value);
} /* end ros3_reset_stats() */
#endif /* ROS3_STATS */


/*-------------------------------------------------------------------------
 *
 * Function: H5FD__ros3_open()
 *
 * Purpose:
 *
 *     Create and/or opens a file as an HDF5 file.
 *
 *     Any flag except H5F_ACC_RDONLY will cause an error.
 *
 *     Name (as received from `H5FD_open()`) must conform to web url:
 *         NAME   :: HTTP "://" DOMAIN [PORT] ["/" [URI] [QUERY] ]
 *         HTTP   :: "http" [ "s" ]
 *         DOMAIN :: e.g., "mybucket.host.org"
 *         PORT   :: ":" <number> (e.g., ":9000" )
 *         URI    :: <string> (e.g., "path/to/resource.hd5" )
 *         QUERY  :: "?" <string> (e.g., "arg1=param1&arg2=param2")
 *
 * Return:
 *
 *     Success: A pointer to a new file data structure.
 *              The public fields will be initialized by the caller, which is
 *              always H5FD_open().
 *
 *     Failure: NULL
 *
 * Programmer: Jacob Smith
 *             2017-11-02
 *
 *-------------------------------------------------------------------------
 */
static H5FD_t *
H5FD__ros3_open(const char *url, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
{
    H5FD_ros3_t      *file      = NULL;
    struct tm        *now       = NULL;
    char              iso8601now[ISO8601_SIZE];
    unsigned char     signing_key[SHA256_DIGEST_LENGTH];
    s3r_t            *handle    = NULL;
    H5FD_ros3_fapl_t  fa;
    H5FD_t           *ret_value = NULL;

    FUNC_ENTER_STATIC

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_open() called.\n");
#endif

    /* Sanity check on file offsets */
    HDcompile_assert(sizeof(HDoff_t) >= sizeof(size_t));

    /* Check arguments */
    if(!url || !*url)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid file name")
    if(0 == maxaddr || HADDR_UNDEF == maxaddr)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, NULL, "bogus maxaddr")
    if(ADDR_OVERFLOW(maxaddr))
        HGOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, NULL, "bogus maxaddr")
    if(flags != H5F_ACC_RDONLY)
        HGOTO_ERROR(H5E_ARGS, H5E_UNSUPPORTED, NULL, "only Read-Only access allowed")

    if(FAIL == H5Pget_fapl_ros3(fapl_id, &fa))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "can't get property list")

    if(CURLE_OK != curl_global_init(CURL_GLOBAL_DEFAULT))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "unable to initialize curl global (placeholder flags)")

    /* open file; procedure depends on whether or not the fapl instructs to
     * authenticate requests or not.
     */
    if(fa.authenticate == TRUE) {
        /* compute signing key (part of AWS/S3 REST API)
         * can be re-used by user/key for 7 days after creation.
         * find way to re-use/share
         */
        now = gmnow();
        HDassert( now != NULL );
        if(ISO8601NOW(iso8601now, now) != (ISO8601_SIZE - 1))
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "problem while writing iso8601 timestamp")
        if(FAIL == H5FD_s3comms_signing_key(signing_key, (const char *)fa.secret_key,
                (const char *)fa.aws_region, (const char *)iso8601now))
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "problem while computing signing key")

        handle = H5FD_s3comms_s3r_open( url, (const char *)fa.aws_region,
                 (const char *)fa.secret_id, (const unsigned char *)signing_key);
    }
    else
        handle = H5FD_s3comms_s3r_open(url, NULL, NULL, NULL);

    if(handle == NULL)
        /* If we want to check CURL's say on the matter in a controlled
         * fashion, this is the place to do it, but would need to make a
         * few minor changes to s3comms `s3r_t` and `s3r_read()`.
         */
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "could not open");

    /* create new file struct */
    file = H5FL_CALLOC(H5FD_ros3_t);
    if(file == NULL)
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "unable to allocate file struct")

    file->s3r_handle = handle;
    HDmemcpy(&(file->fa), &fa, sizeof(H5FD_ros3_fapl_t));

#if ROS3_STATS
    if(FAIL == ros3_reset_stats(file))
        HGOTO_ERROR(H5E_INTERNAL, H5E_UNINITIALIZED, NULL, "unable to reset file statistics")
#endif /* ROS3_STATS */

    ret_value = (H5FD_t*)file;

done:
    if(ret_value == NULL) {
        if(handle != NULL)
            if(FAIL == H5FD_s3comms_s3r_close(handle))
                HDONE_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, NULL, "unable to close s3 file handle")
        if(file != NULL)
            file = H5FL_FREE(H5FD_ros3_t, file);
        curl_global_cleanup(); /* early cleanup because open failed */
    } /* end if null return value (error) */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__ros3_open() */

#if ROS3_STATS
/*----------------------------------------------------------------------------
 *
 * Function: ros3_fprint_stats()
 *
 * Purpose:
 *
 *     Tabulate and pretty-print statistics for this virtual file.
 *
 *     Should be called upon file close.
 *
 *     Shows number of reads and bytes read, broken down by
 *     "raw" (H5FD_MEM_DRAW)
 *     or "meta" (any other flag)
 *
 *     Prints filename and listing of total number of reads and bytes read,
 *     both as a grand total and separate  meta- and rawdata reads.
 *
 *     If any reads were done, prints out two tables:
 *
 *     1. overview of raw- and metadata reads
 *         - min (smallest size read)
 *         - average of size read
 *             - k,M,G suffixes by powers of 1024 (2^10)
 *         - max (largest size read)
 *     2. tabulation of "bins", sepraring reads into exponentially-larger
 *        ranges of size.
 *         - columns for number of reads, total bytes, and average size, with
 *           separate sub-colums for raw- and metadata reads.
 *         - each row represents one bin, identified by the top of its range
 *
 *     Bin ranges can be modified with pound-defines at the top of this file.
 *
 *     Bins without any reads in their bounds are not printed.
 *
 *     An "overflow" bin is also present, to catch "big" reads.
 *
 *     Output for all bins (and range ceiling and average size report)
 *     is divied by powers of 1024. By corollary, four digits before the decimal
 *     is valid.
 *
 *     - 41080 bytes is represented by 40.177k, not 41.080k
 *     - 1004.831M represents approx. 1052642000 bytes
 *
 * Return:
 *
 *     - SUCCESS: `SUCCEED`
 *     - FAILURE: `FAIL`
 *         - occurs if the file passed in is invalid
 *         - TODO: if stream is invalid? how can we check this?
 *
 * Programmer: Jacob Smith
 *
 *----------------------------------------------------------------------------
 */
static herr_t
ros3_fprint_stats(FILE *stream, const H5FD_ros3_t *file)
{
    herr_t             ret_value    = SUCCEED;
    parsed_url_t      *purl         = NULL;
    unsigned           i            = 0;
    unsigned long      count_meta   = 0;
    unsigned long      count_raw    = 0;
    double             average_meta = 0.0;
    double             average_raw  = 0.0;
    unsigned long long min_meta   = (unsigned long long)ROS3_STATS_STARTING_MIN;
    unsigned long long min_raw    = (unsigned long long)ROS3_STATS_STARTING_MIN;
    unsigned long long max_meta     = 0;
    unsigned long long max_raw      = 0;
    unsigned long long bytes_raw    = 0;
    unsigned long long bytes_meta   = 0;
    double             re_dub       = 0.0; /* re-usable double variable */
    unsigned           suffix_i     = 0;
    const char         suffixes[]   = { ' ', 'K', 'M', 'G', 'T', 'P' };

    FUNC_ENTER_STATIC

    if(stream == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "file stream cannot be null" );
    if(file == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "file cannot be null");
    if(file->s3r_handle == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "s3 request handle cannot be null");
    if(file->s3r_handle->purl == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "parsed url structure cannot be null");
    purl = file->s3r_handle->purl;

    /******************
     * PRINT FILENAME *
     ******************/

    HDfprintf(stream, "stats for %s://%s", purl->scheme, purl->host);
    if(purl->port != NULL && purl->port[0] != '\0')
        HDfprintf(stream, ":%s", purl->port);
    if(purl->query != NULL && purl->query[0] != '\0') {
        if (purl->path != NULL && purl->path[0] != '\0')
            HDfprintf(stream, "/%s", purl->path);
        else
            HDfprintf(stream, "/");
        HDfprintf(stream, "?%s", purl->query);
    } else if (purl->path != NULL && purl->path[0] != '\0') {
        HDfprintf(stream, "/%s", purl->path);
    }
    HDfprintf(stream, "\n");

    /*******************
     * AGGREGATE STATS *
     *******************/

    for(i = 0; i <= ROS3_STATS_BIN_COUNT; i++) {
        const ros3_statsbin *r = &file->raw[i];
        const ros3_statsbin *m = &file->meta[i];

        if(m->min < min_meta)
            min_meta = m->min;
        if(r->min < min_raw)
            min_raw  = r->min;
        if(m->max > max_meta)
            max_meta = m->max;
        if(r->max > max_raw)
            max_raw  = r->max;

        count_raw  += r->count;
        count_meta += m->count;
        bytes_raw  += r->bytes;
        bytes_meta += m->bytes;
    }
    if(count_raw > 0)
        average_raw = (double)bytes_raw / (double)count_raw;
    if(count_meta > 0)
        average_meta = (double)bytes_meta / (double)count_meta;

    /******************
     * PRINT OVERVIEW *
     ******************/

    HDfprintf(stream, "TOTAL READS: %llu  (%llu meta, %llu raw)\n",
              count_raw + count_meta, count_meta, count_raw);
    HDfprintf(stream, "TOTAL BYTES: %llu  (%llu meta, %llu raw)\n",
              bytes_raw + bytes_meta, bytes_meta, bytes_raw);

    if(count_raw + count_meta == 0)
        goto done;

    /*************************
     * PRINT AGGREGATE STATS *
     *************************/

    HDfprintf(stream, "SIZES     meta      raw\n");
    HDfprintf(stream, "  min ");
    if(count_meta == 0)
        HDfprintf(stream, "   0.000  ");
    else {
        re_dub = (double)min_meta;
        for(suffix_i = 0; re_dub >= 1024.0; suffix_i++)
            re_dub /= 1024.0;
        HDassert(suffix_i < sizeof(suffixes));
        HDfprintf(stream, "%8.3lf%c ", re_dub, suffixes[suffix_i]);
    }

    if(count_raw == 0)
        HDfprintf(stream, "   0.000 \n");
    else {
        re_dub = (double)min_raw;
        for(suffix_i = 0; re_dub >= 1024.0; suffix_i++)
            re_dub /= 1024.0;
        HDassert(suffix_i < sizeof(suffixes));
        HDfprintf(stream, "%8.3lf%c\n", re_dub, suffixes[suffix_i]);
    }

    HDfprintf(stream, "  avg ");
    re_dub = (double)average_meta;
    for(suffix_i = 0; re_dub >= 1024.0; suffix_i++)
        re_dub /= 1024.0;
    HDassert(suffix_i < sizeof(suffixes));
    HDfprintf(stream, "%8.3lf%c ", re_dub, suffixes[suffix_i]);

    re_dub = (double)average_raw;
    for(suffix_i = 0; re_dub >= 1024.0; suffix_i++)
        re_dub /= 1024.0;
    HDassert(suffix_i < sizeof(suffixes));
    HDfprintf(stream, "%8.3lf%c\n", re_dub, suffixes[suffix_i]);

    HDfprintf(stream, "  max ");
    re_dub = (double)max_meta;
    for(suffix_i = 0; re_dub >= 1024.0; suffix_i++)
        re_dub /= 1024.0;
    HDassert(suffix_i < sizeof(suffixes));
    HDfprintf(stream, "%8.3lf%c ", re_dub, suffixes[suffix_i]);

    re_dub = (double)max_raw;
    for(suffix_i = 0; re_dub >= 1024.0; suffix_i++)
        re_dub /= 1024.0;
    HDassert(suffix_i < sizeof(suffixes));
    HDfprintf(stream, "%8.3lf%c\n", re_dub, suffixes[suffix_i]);

    /******************************
     * PRINT INDIVIDUAL BIN STATS *
     ******************************/

    HDfprintf(stream,
        "BINS             # of reads      total bytes         average size\n");
    HDfprintf(stream,
        "    up-to      meta     raw     meta      raw       meta      raw\n");

    for(i = 0; i <= ROS3_STATS_BIN_COUNT; i++) {
        const ros3_statsbin *m;
        const ros3_statsbin *r;
        unsigned long long   range_end = 0;
        char                 bm_suffix = ' '; /* bytes-meta */
        double               bm_val    = 0.0;
        char                 br_suffix = ' '; /* bytes-raw */
        double               br_val    = 0.0;
        char                 am_suffix = ' '; /* average-meta */
        double               am_val    = 0.0;
        char                 ar_suffix = ' '; /* average-raw */
        double               ar_val    = 0.0;

        m = &file->meta[i];
        r = &file->raw[i];
        if(r->count == 0 && m->count == 0)
            continue;

        range_end = ros3_stats_boundaries[i];

        if(i == ROS3_STATS_BIN_COUNT) {
            range_end = ros3_stats_boundaries[i-1];
            HDfprintf(stream, ">");
        }
        else
            HDfprintf(stream, " ");

        bm_val = (double)m->bytes;
        for(suffix_i = 0; bm_val >= 1024.0; suffix_i++)
            bm_val /= 1024.0;
        HDassert(suffix_i < sizeof(suffixes));
        bm_suffix = suffixes[suffix_i];

        br_val = (double)r->bytes;
        for(suffix_i = 0; br_val >= 1024.0; suffix_i++)
            br_val /= 1024.0;
        HDassert(suffix_i < sizeof(suffixes));
        br_suffix = suffixes[suffix_i];

        if(m->count > 0)
            am_val = (double)(m->bytes) / (double)(m->count);
        for(suffix_i = 0; am_val >= 1024.0; suffix_i++)
            am_val /= 1024.0;
        HDassert(suffix_i < sizeof(suffixes));
        am_suffix = suffixes[suffix_i];

        if(r->count > 0)
            ar_val = (double)(r->bytes) / (double)(r->count);
        for(suffix_i = 0; ar_val >= 1024.0; suffix_i++)
            ar_val /= 1024.0;
        HDassert(suffix_i < sizeof(suffixes));
        ar_suffix = suffixes[suffix_i];

        re_dub = (double)range_end;
        for(suffix_i = 0; re_dub >= 1024.0; suffix_i++)
            re_dub /= 1024.0;
        HDassert(suffix_i < sizeof(suffixes));

        HDfprintf(stream,
                  " %8.3f%c %7d %7d %8.3f%c %8.3f%c %8.3f%c %8.3f%c\n",
                  re_dub, suffixes[suffix_i], /* bin ceiling      */
                  m->count,                   /* metadata reads   */
                  r->count,                   /* rawdata reads    */
                  bm_val, bm_suffix,          /* metadata bytes   */
                  br_val, br_suffix,          /* rawdata bytes    */
                  am_val, am_suffix,          /* metadata average */
                  ar_val, ar_suffix);         /* rawdata average  */

        HDfflush(stream);
    }

done:
    FUNC_LEAVE_NOAPI(ret_value);

} /* ros3_fprint_stats */
#endif /* ROS3_STATS */


/*-------------------------------------------------------------------------
 *
 * Function: H5FD__ros3_close()
 *
 * Purpose:
 *
 *     Close an HDF5 file.
 *
 * Return:
 *
 *     SUCCEED/FAIL
 *
 * Programmer: Jacob Smith
 *             2017-11-02
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__ros3_close(H5FD_t H5_ATTR_UNUSED *_file)
{
    H5FD_ros3_t *file      = (H5FD_ros3_t *)_file;
    herr_t       ret_value = SUCCEED;

    FUNC_ENTER_STATIC

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_close() called.\n");
#endif

    /* Sanity checks */
    HDassert(file != NULL);
    HDassert(file->s3r_handle != NULL);

    /* Close the underlying request handle
     */
    if(FAIL == H5FD_s3comms_s3r_close(file->s3r_handle))
        HGOTO_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "unable to close S3 request handle")

#if ROS3_STATS
    /* TODO: mechanism to re-target stats printout */
    if(ros3_fprint_stats(stdout, file) == FAIL)
        HGOTO_ERROR(H5E_INTERNAL, H5E_ERROR, FAIL, "problem while writing file statistics")
#endif /* ROS3_STATS */

    /* Release the file info */
    file = H5FL_FREE(H5FD_ros3_t, file);

done:
    curl_global_cleanup(); /* cleanup to answer init on open */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__ros3_close() */


/*-------------------------------------------------------------------------
 *
 * Function: H5FD__ros3_cmp()
 *
 * Purpose:
 *
 *     Compares two files belonging to this driver using an arbitrary
 *     (but consistent) ordering:
 *
 *     + url scheme
 *     + url host
 *     + url port
 *     + url path
 *     + url query
 *     + fapl aws_region
 *     + fapl secret_id
 *     + fapl secret_key
 *
 *     tl;dr -> check URL, check crentials
 *
 * Return:
 *
 *     - Equivalent:      0
 *     - Not Equivalent: -1
 *
 * Programmer: Jacob Smith
 *             2017-11-06
 *
 *-------------------------------------------------------------------------
 */
static int
H5FD__ros3_cmp(const H5FD_t *_f1, const H5FD_t *_f2)
{
    const H5FD_ros3_t  *f1        = (const H5FD_ros3_t *)_f1;
    const H5FD_ros3_t  *f2        = (const H5FD_ros3_t *)_f2;
    const parsed_url_t *purl1     = NULL;
    const parsed_url_t *purl2     = NULL;
    int                 ret_value = 0;

    FUNC_ENTER_STATIC_NOERR

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_cmp() called.\n");
#endif

    HDassert(f1->s3r_handle != NULL);
    HDassert(f2->s3r_handle != NULL);

    purl1 = (const parsed_url_t *)f1->s3r_handle->purl;
    purl2 = (const parsed_url_t *)f2->s3r_handle->purl;
    HDassert(purl1 != NULL);
    HDassert(purl2 != NULL);
    HDassert(purl1->scheme != NULL);
    HDassert(purl2->scheme != NULL);
    HDassert(purl1->host != NULL);
    HDassert(purl2->host != NULL);

    /* URL: SCHEME */
    if(HDstrcmp(purl1->scheme, purl2->scheme))
        HGOTO_DONE(-1)

    /* URL: HOST */
    if(HDstrcmp(purl1->host, purl2->host))
        HGOTO_DONE(-1)

    /* URL: PORT */
    if(purl1->port && purl2->port) {
        if(HDstrcmp(purl1->port, purl2->port))
            HGOTO_DONE(-1)
    }
    else if(purl1->port)
        HGOTO_DONE(-1)
    else if(purl2->port)
        HGOTO_DONE(-1)

    /* URL: PATH */
    if(purl1->path && purl2->path) {
        if(HDstrcmp(purl1->path, purl2->path))
            HGOTO_DONE(-1)
    }
    else if(purl1->path && !purl2->path)
        HGOTO_DONE(-1)
    else if(purl2->path && !purl1->path)
        HGOTO_DONE(-1)

    /* URL: QUERY */
    if(purl1->query && purl2->query) {
        if(HDstrcmp(purl1->query, purl2->query))
            HGOTO_DONE(-1)
    }
    else if(purl1->query && !purl2->query)
        HGOTO_DONE(-1)
    else if(purl2->query && !purl1->query)
        HGOTO_DONE(-1)

    /* FAPL: AWS_REGION */
    if(f1->fa.aws_region[0] != '\0' && f2->fa.aws_region[0] != '\0') {
        if(HDstrcmp(f1->fa.aws_region, f2->fa.aws_region))
            HGOTO_DONE(-1)
    }
    else if(f1->fa.aws_region[0] != '\0')
        HGOTO_DONE(-1)
    else if(f2->fa.aws_region[0] != '\0')
        HGOTO_DONE(-1)

    /* FAPL: SECRET_ID */
    if(f1->fa.secret_id[0] != '\0' && f2->fa.secret_id[0] != '\0') {
        if(HDstrcmp(f1->fa.secret_id, f2->fa.secret_id))
            HGOTO_DONE(-1)
    }
    else if(f1->fa.secret_id[0] != '\0')
        HGOTO_DONE(-1)
    else if(f2->fa.secret_id[0] != '\0')
        HGOTO_DONE(-1)

    /* FAPL: SECRET_KEY */
    if(f1->fa.secret_key[0] != '\0' && f2->fa.secret_key[0] != '\0') {
        if(HDstrcmp(f1->fa.secret_key, f2->fa.secret_key))
            HGOTO_DONE(-1)
    }
    else if(f1->fa.secret_key[0] != '\0')
        HGOTO_DONE(-1)
    else if(f2->fa.secret_key[0] != '\0')
        HGOTO_DONE(-1)

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* H5FD__ros3_cmp() */


/*-------------------------------------------------------------------------
 * Function:    H5FD__ros3_query
 *
 * Purpose:     Set the flags that this VFL driver is capable of supporting.
 *              (listed in H5FDpublic.h)
 *
 *              Note that since the ROS3 VFD is read only, most flags
 *              are irrelevant.
 *
 *              The term "set" is highly misleading...
 *              stores/copies the supported flags in the out-pointer `flags`.
 *
 * Return:      SUCCEED (Can't fail)
 *
 * Programmer:  John Mainzer
 *              9/11/17
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__ros3_query(const H5FD_t H5_ATTR_UNUSED *_file, unsigned long *flags)
{
    FUNC_ENTER_STATIC_NOERR

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_query() called.\n");
#endif

    /* Set the VFL feature flags that this driver supports */
    if(flags) {
        *flags = 0;
        /* OK to perform data sieving for faster raw data reads & writes */
        *flags |= H5FD_FEAT_DATA_SIEVE;
    } /* end if */

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5FD__ros3_query() */


/*-------------------------------------------------------------------------
 *
 * Function: H5FD__ros3_get_eoa()
 *
 * Purpose:
 *
 *     Gets the end-of-address marker for the file. The EOA marker
 *     is the first address past the last byte allocated in the
 *     format address space.
 *
 * Return:
 *
 *     The end-of-address marker.
 *
 * Programmer: Jacob Smith
 *             2017-11-02
 *
 *-------------------------------------------------------------------------
 */
static haddr_t
H5FD__ros3_get_eoa(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type)
{
    const H5FD_ros3_t *file = (const H5FD_ros3_t *)_file;

    FUNC_ENTER_STATIC_NOERR

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_get_eoa() called.\n");
#endif

    FUNC_LEAVE_NOAPI(file->eoa)
} /* end H5FD__ros3_get_eoa() */


/*-------------------------------------------------------------------------
 *
 * Function: H5FD__ros3_set_eoa()
 *
 * Purpose:
 *
 *     Set the end-of-address marker for the file.
 *
 * Return:
 *
 *      SUCCEED  (can't fail)
 *
 * Programmer: Jacob Smith
 *             2017-11-03
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__ros3_set_eoa(H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type, haddr_t addr)
{
    H5FD_ros3_t *file = (H5FD_ros3_t *)_file;

    FUNC_ENTER_STATIC_NOERR

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_set_eoa() called.\n");
#endif

    file->eoa = addr;

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5FD__ros3_set_eoa() */


/*-------------------------------------------------------------------------
 *
 * Function: H5FD__ros3_get_eof()
 *
 * Purpose:
 *
 *     Returns the end-of-file marker.
 *
 * Return:
 *
 *     EOF: the first address past the end of the "file", either the
 *     filesystem file or the HDF5 file.
 *
 * Programmer: Jacob Smith
 *             2017-11-02
 *
 *-------------------------------------------------------------------------
 */
static haddr_t
H5FD__ros3_get_eof(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type)
{
    const H5FD_ros3_t *file = (const H5FD_ros3_t *)_file;

    FUNC_ENTER_STATIC_NOERR

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_get_eof() called.\n");
#endif

    FUNC_LEAVE_NOAPI(H5FD_s3comms_s3r_get_filesize(file->s3r_handle))
} /* end H5FD__ros3_get_eof() */


/*-------------------------------------------------------------------------
 *
 * Function: H5FD__ros3_get_handle()
 *
 * Purpose:
 *
 *     Returns the S3 Request handle (s3r_t) of ros3 file driver.
 *
 * Returns:
 *
 *     SUCCEED/FAIL
 *
 * Programmer: Jacob Smith
 *             2017-11-02
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__ros3_get_handle(H5FD_t *_file, hid_t H5_ATTR_UNUSED fapl,
    void **file_handle)
{
    H5FD_ros3_t *file      = (H5FD_ros3_t *)_file;
    herr_t       ret_value = SUCCEED;

    FUNC_ENTER_STATIC

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_get_handle() called.\n");
#endif

    if(!file_handle)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "file handle not valid")

    *file_handle = file->s3r_handle;

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__ros3_get_handle() */


/*-------------------------------------------------------------------------
 *
 * Function: H5FD__ros3_read()
 *
 * Purpose
 *
 *     Reads SIZE bytes of data from FILE beginning at address ADDR
 *     into buffer BUF according to data transfer properties in DXPL_ID.
 *
 * Return:
 *
 *     Success: `SUCCEED`
 *         - Result is stored in caller-supplied buffer BUF.
 *     Failure: `FAIL`
 *         - Unable to complete read.
 *         - Contents of buffer `buf` are undefined.
 *
 * Programmer: Jacob Smith
 *             2017-11-??
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__ros3_read(H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type,
    hid_t H5_ATTR_UNUSED dxpl_id, haddr_t addr, size_t size,
    void *buf)
{
    H5FD_ros3_t *file      = (H5FD_ros3_t *)_file;
    size_t       filesize  = 0;
    herr_t       ret_value = SUCCEED;
#if ROS3_STATS
    /* working variables for storing stats */
    ros3_statsbin *bin   = NULL;
    unsigned       bin_i = 0;
#endif /* ROS3_STATS */


    FUNC_ENTER_STATIC

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_read() called.\n");
#endif

    HDassert(file != NULL);
    HDassert(file->s3r_handle != NULL);
    HDassert(buf != NULL);

    filesize = H5FD_s3comms_s3r_get_filesize(file->s3r_handle);

    if((addr > filesize) || ((addr + size) > filesize))
        HGOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, FAIL, "range exceeds file address")

    if(H5FD_s3comms_s3r_read(file->s3r_handle, addr, size, buf) == FAIL)
        HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "unable to execute read")

#if ROS3_STATS

    /* Find which "bin" this read fits in. Can be "overflow" bin.  */
    for(bin_i = 0; bin_i < ROS3_STATS_BIN_COUNT; bin_i++)
        if((unsigned long long)size < ros3_stats_boundaries[bin_i])
            break;
    bin = (type == H5FD_MEM_DRAW)
        ? &file->raw[bin_i]
        : &file->meta[bin_i];

    /* Store collected stats in appropriate bin */
    if(bin->count == 0) {
        bin->min = size;
        bin->max = size;
    }
    else {
        if(size < bin->min)
            bin->min = size;
        if(size > bin->max)
            bin->max = size;
    }
    bin->count++;
    bin->bytes += (unsigned long long)size;

#endif /* ROS3_STATS */

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__ros3_read() */


/*-------------------------------------------------------------------------
 *
 * Function: H5FD__ros3_write()
 *
 * Purpose:
 *
 *     Write bytes to file.
 *     UNSUPPORTED IN READ-ONLY ROS3 VFD.
 *
 * Return:
 *
 *     FAIL (Not possible with Read-Only S3 file.)
 *
 * Programmer: Jacob Smith
 *             2017-10-23
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__ros3_write(H5FD_t H5_ATTR_UNUSED *_file, H5FD_mem_t H5_ATTR_UNUSED type,
    hid_t H5_ATTR_UNUSED dxpl_id, haddr_t H5_ATTR_UNUSED addr,
    size_t H5_ATTR_UNUSED size, const void H5_ATTR_UNUSED *buf)
{
    herr_t ret_value = FAIL;

    FUNC_ENTER_STATIC

#if ROS3_DEBUG
    HDfprintf(stdout, "H5FD__ros3_write() called.\n");
#endif

    HGOTO_ERROR(H5E_VFL, H5E_UNSUPPORTED, FAIL, "cannot write to read-only file.")

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* H5FD__ros3_write() */


/*-------------------------------------------------------------------------
 *
 * Function: H5FD__ros3_truncate()
 *
 * Purpose:
 *
 *     Makes sure that the true file size is the same (or larger)
 *     than the end-of-address.
 *
 *     NOT POSSIBLE ON READ-ONLY S3 FILES.
 *
 * Return:
 *
 *     FAIL (Not possible on Read-Only S3 files.)
 *
 * Programmer: Jacob Smith
 *             2017-10-23
 *
 *-------------------------------------------------------------------------
 */